Python/파이선과 친해지기

[Python] - Python과 매우 친해지기-클래스의 사용

지난 시간 우리는 객체 지향 프로그램이 무엇이고, 클래스가 무엇인지, 그리고 간단하게 클래스 사용의 예시를 살펴보았다. 이번 시간에는 Python에서 클래스를 사용하는 방법을 자세하게 알아보도록 하자


클래스(Class)의 정의와 사용

우선 다음의 코드를 타이핑하고 이야기를 나누어보자. 주석은 각 코드에 대한 설명이니 한번씩 읽어보길 바란다.

### class_example.py ###

class dog :					## class의 이름을 dog로 지정하였다.
    dog_name = ""				## dog의 멤버변수 dog_name을 정의하고 ""로 초기화

    def __init__(self,name) :			## 생생자, name을 파라미터로 받아 dog_name대입
        self.dog_name = name

    def bark(self) :				## 생성된 객체에서 호출하는 bark
        print("WOW "+self.dog_name)

    def self_bark() :				## class에서 호출하는 bark
        print("BOW")

if __name__ == "__main__" :
    my_dog = dog("puppy")			## my_dog(객체)를 dog(클래스)로 생성
    my_dog.bark()				## 생성된 my_dog의 bark메서드 호출

    dog.self_bark()				## dog(클래스)에서 직접 self_bark메서드 호출
    
    
출력 :
WOW puppy
BOW

self

일단 이 self 예약어는 어떤 의미가 있는 것일까?

Python에서 객체를 생성을 하면 자기자신을 지칭할 때 self라고 한다. 재미있는 점은 class내부에서 메서드를 정의할 때  생성된 객체가 이 메서드를 사용하려면 암묵적으로 self를 첫 번째 파라미터로 넘겨야 한다는 점이다.(실제 사용 시에는 이 self는 없는 것으로 취급한다. 예를 들어 some_function(self)가 정의되어있으면 사용할 때는 some_funtion()으로 사용하면 된다는 것이다.)

 

비교를 위해서 코드에서 2가지 함수를 다음과 같이 정의해보았다.

1. bark(self) : self를 인자로 받는 메서드이다. self를 인자로 받기에, dog에서 객체를 직접 넘겨주거나 dog로 생성이된 객체에서만(위 예시에서는 my_dog)이 bark를 사용할 수 있다.

2. self_bark() : self를 인자로 받지 않는 메서드이다. 따라서 생성이 된 객체는 사용할 수 없으며(생성된 객체는 함수호출시 묵시적으로 첫번째파라미터로 자기 자신을 전달한다. self_bark는 self파라미터를 받지 않는다.) dog클래스에서 직접 호출하면 사용 가능하다.

 

생성자

처음 보는 메서드로 __init__(self, name)이라는 메서드가 눈에 뜨인다. 이 __init__메서드는 이미 정의된 것으로 객체가 생성되는 순간(위 코드에서는 my_dog = dog("puppy")가 처리되는 순간)에서 자동적으로 동작하는 함수를 의미한다. 객체가 생성되는 순간 동작하는 함수이기에 "생성자"라고 부른다. 이 생성자에도 파라미터를 넘길 수 있는데, 위 예제에서는 객체 생성에 사용될 메서드이니 self와 이름인 name을 파라미터로 전달하고 있다. 전달받은 name변수는 dog_name에 저장한다. Python에 생성자처럼 사전 정의된 다음과 같은 것들도 있다.

1. __del__(self) : 소멸자, 객체가 사라질 때 행동을 정의한다.

2. __repr__(self) : 프린팅 지시자, print로 객체 자신이 호출될 때 return 값으로 무엇을 출력할지 지시한다.

3. __add__(self,other) : 연산자 "+"가 객체에 취해질 때 행동을 정의한다.

4. __cmp__(self,other) : 비교 연산자를 객체에 사용할 수 있게 행동을 정의한다.

 

클래스 변수

아래 코드에서 dog클래스 바로 아래 dog_name, travel처럼 정의된 변수를 클래스 변수라고 한다.

객체는 자신의 변수를 접근할 때 self. [변수명]을 붙인다. 코드에서 new_puppy = dog("happy")를 했다면 my_dog.dog_name은 "puppy", new_puppy.dog_name은 "happy"가 된다.(dog.dog_name은 ""로 출력이 된다)

 

이 클래스 변수는 재미있는 특징이 있다. 다음의 코드 main을 보자

class dog :
    dog_name = ""
    travel = list()

    def __init__(self,name) :
        self.dog_name = name

    def bark(self) :
        print("WOW "+self.dog_name)

    def self_bark() :
        print("BOW")

    def add_travel(self, value) :
        self.travel.append(value)

if __name__ == "__main__" :
    my_dog = dog("puppy")
    new_dog = dog("happy")

    my_dog.add_travel("home")
    

가장 마지막의 my_dog.add_travel("home")을 통해서 my_dog의 travel list에 "home"을 추가해 주었다. 그런데 웬걸?

>> new_dog.travel
["home"]
>> dog.travel
["home"]

이상하게 다른 객체인 new_dog와 객체의 틀인 dog클래스의 travel list에도 "home"이 추가된 걸 확인할 수 있다.

 

이는 클래스가 언제 객체로 바뀌느냐(이를 인스턴스화라고 한다.)의 문제이다.

 

Python에서 리터럴을 제외한 자료구조와 변수들은 class 바로 아래 정의를 했다면 객체끼리 공유된다. 이는 생성자에 자료구조를 정의하는 것으로 해결된다. 위에 코드로 해결책을 예를 들면 생성자에 self.travel = list()로 정의한다.(이러면 객체가 생성(인스턴스화)이 되고 self.travel이라는 list가 생성이 된 것이니 객체끼리 공유되지 않고, 생성된 객체 고유의 자료구조로 본다.)


이번 시간에는 객체 지향 프로그래밍을 하기 위한 가장 기본적인 방법인 클래스 생성과 사용에 대해서 알아보았다. 다음 시간에는 객체 지향 프로그래밍에서 빼놓은 수 없는 개념인 상속과 python에서의 상속 사용을 알아보도록 하자