class C1:
= 23 x
15 객체 지향형 파이썬(OOP
)
파이썬은 객체 지향형 언어의 일종이다.
15.1 클래스
- 파이썬 클래스(class)는 (함수처럼) 호출 가능하고, 클래스를 호출하여 인스턴스(instance) 객체를 만든다. 이 과정을
instantiation
이라고 한다. 인스턴스의 타입(type)은 클래스이다. - 클래스는 임의의 attributes를 가진다.
- 클래스 attributes의 값으로 정의된 함수를 메서드(methods)라고 한다.
- 모든 인스턴스 객체는 attribute lookup을 할 때 먼저 인스턴스 안에서 검색을 하고, 만약 없으면서 자신의 class로 옮겨 검색을 한다. 만약 이 class에서도 발견되지 않으면 부모 class로 이동하여 검색한다.
- 파이썬에서 클래스는 하나의 객체(value)이다. 따라서 다른 객체와 똑같이 취급된다(first-class objects).
- 함수를 호출할 때 argument로 클래스를 전달할 수 있다.
- 함수 호출의 결과로 클래스를 반환할 수 있다.
- 변수에 할당할 수 있다.
- 컨테이너의 하나의 아이템으로 들어갈 수 있고, 객체의 한 attributes로 지정될 수 있다.
- 딕셔너리에서 키로 사용할 수 있다.
15.2 클래스 정의하는 방법
클래스 정의는 다음과 같은 문법 구조를 가진다.
class Classname(base-classes, *, **kw): statement(s)
class
로 시작한다.- 클래스 이름은 대문자로 시작하는 것이 관례이다(title-case).
base-classes
는 코마로 구분한다.base-classes
를 지정하지 않는다는 것은object
를 사용한다는 의미이고, 이런 경우에는 괄호를 생략할 수 있다.statesment(s)
부분을 클래스 바디(body)라고 한다.
다음은 C1
이라는 클래스에서 x
라는 attribute를 명시한 예이다. 보는 바와 같이 class body 안에서 이름에 값을 할당하면, 이 이름은 클래스의 attribute로 지정된다.
위 코드가 실행되면, C1
클래스가 x
라는 attributes를 가지게 되고, 그 값은 23
이 된다. 이 attribute의 값에 접근할 때는 dot(.
)를 사용하여 C1.x
라는 문법을 사용한다.
C1.x
23
위 코드가 실행되면 C1
이라는 클래스가 정의되지만 그 인스턴스는 생성된 것은 아니다. 인스턴스는 클래스를 함수 호출과 같은 방식으로 클래스를 호출하여 생성된다.
= C1() c1
이제 c1
라는 C1
클래스의 인스턴스가 만들어졌다. c1
의 type()
을 물어보자. C1
이라고 답할 것이다.
type(c1)
__main__.C1
어떤 클래스의 모든 인스턴스는 클래스 attributes를 공유한다. 다음은 c2
라는 인스턴스를 하나 더 생성했다. c1
이나 c2
모두에서 같은 클래스의 attribute x
에 접근할 수 있다.
= C1()
c2 c1.x, c2.x
(23, 23)
class
문이 실행될 때, 암묵적으로 해당 클래스에 다음과 같은 attributes가 자동으로 만들어진다.
__name__
: 클래스의 이름__bases__
: 부모 클래스들__dict__
: 클래스 attributes를 저장(read-only)
print(C1.__name__)
print(C1.__bases__)
print(C1.__dict__)
C1
(<class 'object'>,)
{'__module__': '__main__', 'x': 23, '__dict__': <attribute '__dict__' of 'C1' objects>, '__weakref__': <attribute '__weakref__' of 'C1' objects>, '__doc__': None}
15.3 메서드(method) 정의
클래스 body 안에서 def
문을 사용하여 만든다. 일반적인 함수 정의 방법과 규칙을 모두 따른다. 단, 첫 번째 파라미터는 인스턴스를 의미하는 self
가 되어야 한다. 다른 단어로 써도 되지만 관례상 self
로 사용한다. 메서드도 클래스 attributes 일종이므로 클래스의 모든 인스턴스에서 공유되고, .
문법을 사용하여 접근할 수 있고, 함수이기 때문에 ()
를 사용하여 호출한다.
class C2:
= 23
x def hello(self):
print(f"내가 가진 x 값은 {self.x}이다.")
= C2()
my_c my_c.hello()
내가 가진 x 값은 23이다.
위에서 보듯이 메서드 안에서 클래스의 attribute를 접근할 때 self.x
를 사용했다. class
는 namespace를 만들기는 하지만 함수처럼 scoping을 제공하지는 않는다. 그래서 온전한 이름을 써야 한다(fully qualified name). 메서드 안에서 클래스의 다른 메서드를 호출하는 경우에도 똑같이 이렇게 사용한다. self.x
대신 C2.x
라고도 할 수 있다. 메서드 밖에서는 아래 y
와 같이 이름 그대로 쓸 수 있다.
class C2:
= 23
x = x + 4
y def hello(self):
print(f"내가 가진 x 값은 {self.x}이다.")
15.4 __init__()
스페셜 메서드
파이썬에는 더블 언더스코어(__
)로 되어 있는 미리 이름이 지정되어 특수한 목적으로 사용되는 메서드들이 있다. 이런 메서드를 스페셜 메서드(special method)라고 하고, 던더(dunder) 메서드라고 하기도 하고, 매직 메서드라고도 한다. 그 가운데 __init__()
메서드는 클래스 정의할 때 가장 많이 사용되는 메서드이 때문에 먼저 알 필요가 있다.
__init__()
메서드는 인스턴스의 초기값을 설정하는 데 사용된다.
다음은 Account
라는 은행 계좌를 클래스로 구현한 예인데, 이 사례를 가지고 알아 보자.
class Account:
def __init__(self, owner, balance):
self.owner = owner
self.balance = balance
def __repr__(self):
return f'Account({self.owner!r}, {self.balance!r})'
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
def inquiry(self):
return self.balance
이렇게 정의된 Account
클래스의 인스턴스를 만들 때는 다음과 같이 실행한다.
= Account("ksb", 10000)
mine mine
Account('ksb', 10000)
이것을 위 __init__()
메서드 정의와 연관지어 볼 수 있어야 한다.
__init__()
첫 번째 파라미터는 다른 메서드와 동일하게 인스턴스를 의미하는self
이다.- 하지만 인스턴스를 만들 때는
self
를 뺀 나머지 파라미터를 맞추어Account("ksb", 10000)
클래스 호출을 실행한다. 이 경우"ksb"
가owner
로,10000
이balance
로 넘겨진다. - 이렇게 넘겨진 값들은
__init__()
메서드 안의 코드에서 따라self.owner
,self.balance
로 할당된다. __init__()
함수의 역할을 딱 여기까지이다. 이 함수는None
값 이외에는 반환값이 없어야 한다.return
을 사용하면 에러(TypeError)가 발생한다. 그래서return
문을 사용하지 않는 것이 관례이다.
a = Account("Guido", 10000)# 이것은 Account(a, 'Guido', 10000)와 같다.
15.5 __repr__()
스페셜 메서드
위 코드에서 다음과 같이 __repr__()
스페셜 메서드가 정의되어 있다.
def __repr__(self): return f'Account({self.owner!r}, {self.balance!r})'
이것은 생성된 인스턴스를 어떻게 문자열로 표시하게 만들지 정의하고자 할 때 사용하는 메서드이다. 사용자가 정의하는 클래스는 일종의 데이터 타입인데, 내장된 리스트나 딕셔너리처럼 문자열로 표시하여 보여주는 것이 유용할 때가 많다. 우리가 만드는 객체의 문자열 표현을 정의하는 것이 __repr__()
메서드의 역할이다.
위 경우 mine
객체는 아래와 같이 표시된다.
mine
Account('ksb', 10000)
이것은 repr()
이라는 내장 함수의 작동 방식을 결정한다.
repr(mine)
"Account('ksb', 10000)"
출력된 것을 잘 관찰해 보면 인스턴스를 만들 때의 코드와 같음을 알 수 있다. 보통 이 출력을 가지고 같은 객체를 만들 수 있게 __repr__()
을 정의한다.
eval(repr(mine))
Account('ksb', 10000)