이 글의 작성자는 C/C++ 프로그래밍을 하던 사람이다. 이 글은 Python을 복습하며 작성하는 글이니, 부족한 부분이 있으면 얼마든지 피드백을 주시기 바란다.

 

클래스(class)

그 동안 특정 객체의 타입이 무엇인지 궁금하면 type() 함수를 통해 객체의 타입을 확인했을 것이다. type() 함수의 결과를 print()를 이용해 출력하면 다음과 같이 나온다.

<class 'int'>

타입이 궁금해서 type()을 사용했는데, type이라는 말 대신, class라는 말이 나오고 있었다. 왜 타입을 물어봤는데 클래스를 알려주는걸까? 이제는 슬슬 클래스가 뭐하는 녀석인지 알아둬야 할 때가 되었다.

 

클래스(class)는 객체를 표현하기 위한 설계도이다. 클래스에서 명시한 정보를 기반으로, 파이썬은 객체를 만들어낸다. 클래스가 명시하는 정보는 많을 수도 있고 적을 수도 있지만, 크게 보면 두 가지로 나눌 수 있을 것이다.

  • 이 객체는 어떤 값들을 가지고 있는가
  • 이 객체는 어떤 행동들을 할 수 있는가

위의 두 항목은 부르는 이름이 따로 있다.

객체가 기지고 있는 값들은 속성(attribute), 혹은 필드(fields)라고라고 부른다.
객체가 할 수 있는 행동, 즉 함수는 메서드(method)라고 부른다.

그래서 보통 속성을 나타내는 이름은 명사형으로, 함수를 나타내는 이름은 동사형으로 짓는다.

속성과 메서드를 적절히 사용하여, 원하고자 하는 데이터를 표현하는 것이 곧 클래스를 제대로 설계하는 방법의 기초라고 할 수 있겠다.

 

클래스 정의하기

그러면 클래스를 하나 정의해보자. 기왕이면 무언가를 나타내는 클래스를 만들어보고 싶으니, 사람에 대해 표현하는 클래스를 만들어보도록 하자.

시작은 가볍게 이름만 있는 클래스를 만들어보도록 하자. 사람을 나타내는 단어인 person을 클래스 이름으로 사용할 것이다.

class person:
    pass

이렇게 하면 아무런 기능도 하지 않는 person라는 클래스를 정의할 수 있다. 비록 지금은 아무런 기능도 들어있지 않긴 하지만. 이제 차근차근 뼈대를 세우고 살을 붙여나가면 된다.

 

정의한 클래스 객체 생성하기

아무런 기능이 없긴 해도 아무튼 person 클래스라는 청사진을 만들었다. 그러면 이 청사진을 건네주며 파이썬에게 ‘이걸 보고 객체를 만들어라!’ 라는 명령을 내려야 한다. 그걸 어떻게 하면 될까?

사실 그 동안 많이 써 왔다. 리스트를 생성할 때 list()를 사용했듯이, 생성하고자 하는 클래스 이름으로 호출하면 된다.

class person:
    pass

p = person()
print(type(p))

그러면 다음과 같은 출력을 보게 될 것이다.

<class '__main__.person'>

앞에 무언가가 붙긴 했는데, person이란 이름의 클래스를 만들었다!

 

클래스로 객체를 생성할 때 수행하는 동작 정하기

클래스 이름으로 호출한다고 하니, 클래스에는 객체를 생성할 때 호출하는 함수 같은 것이 있을 것 같다. 그러면 그것이 무엇일까? 다음 코드를 실행해보자.

class person:
    def __init__(self):
        print('hi!')

p = person()

위의 코드를 실행하면 'hi!'가 출력되는 것을 확인할 수 있을 것이다. 그리고 'hi!'를 출력하는 코드는 이번에 새롭게 정의한 함수, __init__이라는 함수에서 출력하고 있다.

그렇다. 클래스 내부에 __init__이라는 함수를 정의하면, 파이썬은 해당하는 클래스로 객체를 만들 때 __init__함수를 호출해준다. 이것을 이니셜라이저(initializer)라고 부른다.

혹시나 다른 언어를 하고 온 사람이라면, ‘저거 생성자(constructor) 아니야?’ 라는 말을 할 수도 있다. 하지만 생성자는 따로 __new__라는 이름을 가지고 있다.

 

self?

그런데 __init__ 함수에 무언가 눈에 띄는 것이 하나 있다. self라는 매개변수가 떡하니 자리 잡고 있다. 우리는 person()을 호출할 때 전달한 인자가 하나도 없다. 그런데 __init__은 무언가를 받고 있다. 그렇다고 오류가 발생하는 것도 아니다. 이 녀석의 정체는 무엇일까?

궁금한 것이 있다면, 출력해보면 된다. 출력해보자.

class person:
    def __init__(self):
        print(self)

p = person()

그러면 다음과 비슷한 문구가 출력될 것이다. 언제나처럼 뒤의 숫자가 약간 다를 것이다.

<__main__.person object at 0x032CB148>

대충 보니 person 클래스의 객체라는 뜻 같다. 객체라고 하니, person()을 통해 새로운 객체를 만든다고 했었다. 그러면 self는 새로 만들어진 person 클래스의 객체를 의미하는 것일까? 코드 한 줄을 추가해보자.

class person:
    def __init__(self):
        print(self)

p = person()
print(p)

이 코드를 실행하면 다음과 비슷한 문구가 출력될 것이다.

<__main__.person object at 0x0138B148>
<__main__.person object at 0x0138B148>

‘object at’ 뒤에 붙은 숫자들까지 똑같은걸 보니, __init__함수에 들어오는 self와 이번에 새로 생성한 person 클래스의 객체인 p는 같은 객체인 것 같다. id() 함수로 확인해도 같은 숫자가 나오는 것을 확인할 수 있을 것이다.

그렇다. self객체 자기 자신을 의미하는 것이었다. 그렇다면 person 객체를 하나 더 만들면, 그 객체에서 보이는 숫자는 또 달라질 것이라고 예측할 수 있다.

class person:
    def __init__(self):
        print(self)

p = person()
print(p)

s = person()
print(s)

출력해보면 다음과 비슷하게 나올 것이다.

<__main__.person object at 0x0191B118>
<__main__.person object at 0x0191B118>
<__main__.person object at 0x0191B148>
<__main__.person object at 0x0191B148>

 

그러면 또 하나의 의문이 들 수 있다. self매개 변수일텐데, 이름을 바꿀 수 없는가?

바꿀 수 있다. selfoooo라는 이름으로 바꿔도 잘 동작한다. 하지만, 되도록이면 바꾸지 않기를 강력하게 권장한다. 생각해보면 굳이 바꿀 이유도 없다. 특히나 이 self라는 이름은 객체 자기 자신을 가리키는 관용적인 이름으로 쓰이고 있기 때문에, 이 외의 이름으로 바꿨다가는 괜히 헷갈릴 수 있다.
변수의 이름을 보고 이 변수가 어떤 역할을 하는지 파악할 수 있다는 것은 정말로 중요한 요소이다. 괜히 사람들이 이렇게 쓰는 것이 아니니, 얌전히 self라는 이름을 사용하도록 하자.

코드 난독화(code obfuscation)를 하고 싶다면 말리진 않겠다. 본격적인 난독화에 비교할 바는 아니겠지만…

 

객체 속성 추가하기

슬슬 person 클래스에 속성 값을 추가할 때가 되었다. 그러면 이제 본격적으로 객체의 속성을 추가해보자.

속성을 추가하기 전에 생각해보자. person, 즉 사람이 가지고 있는 속성은 무엇이 있을까? 사람 개개인이 가지고 있는 특성은 무엇이 있을까? 대충 생각해보면 다음과 같이 나열할 수 있을 것이다.

  • 이름
  • 나이
  • 성별
  • 체중

계속해서 생각하다 보면 끝도 없이 나올 것이다. 그렇다고 생각나는대로 집어넣으면 안된다. ‘person 클래스로 무엇을 할 것인가?’를 생각하고, 해당하는 일에 필요한 것만 집어넣으면 된다.

그렇다면 이번 예제에서 구현할 person 클래스는 어떤 일을 할 것인가? 라고 묻는다면, 처음에는 간단하게 자기소개를 하는 기능을 추가하려고 한다. 자기소개는 대충 안녕하세요 문구와 자기 이름과 나이를 말하는 식으로 구현해보자.

그러면 객체에 무슨 속성을 추가해야 할지 정해졌다. 이름나이를 표현하는 속성을 만들어보자.

 

그러기 위해서는 __init__ 함수를 조금 수정해야 할 것 같다. person 클래스로 객체를 생성할 때, 이름과 나이를 전달받을 수 있도록 수정하면 될 것이다.

어떻게 하느냐? 그냥 매개변수를 추가하면 된다. 그러면 __init__ 함수에서 전달받은 인자로 나이와 이름을 초기화하는 코드를 살펴보자.

class person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

특이한 점이 하나 보인다. self.name이라고 하고 있다. 왜 이렇게 하고 있는걸까?

지금 정의하고 있는 속성이 self 객체에 정의하고 있다는 것을 표현하기 위해서이다. 그 동안 메서드를 쓰면서 객체 뒤에 무엇을 붙였는가? .을 붙였었다. .객체가 가지고 있는 속성/메서드에 접근하기 위해 사용하는 연산자라고 볼 수 있다. 따라서 지금 생성중인 객체의 속성을 정의하기 위해 self.을 꼭 붙여야 한다.

그렇다고 안붙이면 동작하지 않는가? 라고 물어보면 그건 또 아니다. 이 내용에 대해서는 다음에 알아보자. 지금은 self.를 붙인 상태로 진행을 해 보자.

그러면 직접 person 객체를 생성해서 이름과 나이를 출력해보자.

class person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = person('jack', 23)
print(p.name, p.age)

이제 생성자에 추가로 받는 인자가 생긴 이상, 단순히 person()으로 호출하면 에러가 발생하게 될 것이다. 일일이 인자를 넣는게 귀찮다면, name과 age에 디폴트 인자를 추가하면 된다.

위의 코드를 실행하면 다음과 같은 결과를 확인할 수 있을 것이다.

jack 23

이제 person 클래스로 생성되는 객체는 이름과 나이 정보를 담고 있는 객체가 되었다.

 

Categories:

Updated:

Comments