오늘은 클래스 / 객체 / __new__ / __init__ 에 대해 살펴보도록 하자.
클래스와 객체를 배울 때 단골로 등장하는 내용이 "붕어빵 틀"과 "붕어빵"이다.
클래스를 붕어빵 틀에 비유하고, 객체를 붕어빵 틀에서 생성된 붕어빵으로 비유되곤 한다.
우선 간단하게 클래스와 객체를 Code level에서 살펴보자.
class Python : # python class 정의
def say(self) : # python class의 멤버 함수 정의
print("hello python")
def main() :
p = Python() # python class를 사용해서 객체 p를 생성
p.say() # 객체 p를 사용하여 class의 멤버 함수인 say()를 호출
if __name__ == '__main__' :
main()
__new__ 는 Allocator이다. 즉, Python class의 메모리를 할당하는 역할을 한다.
__init__은 Initializer 이다. 즉, __new__에서 할당된 메모리에 초기 값을 할당하는 역할을 한다.
따라서 __new__는 __init__ 보다 먼저 호출된다.
예제를 통해 살펴보자.
class Python :
def __new__(cls):
print("__new__")
def __init__(self):
print("__init__")
def say(self) :
print("hello python")
def main() :
print(1)
p = Python()
print(2)
p.say()
print(3)
if __name__ == '__main__' :
main()
위 코드를 실행시키면 이런 에러가 발생한다. 왜일까?
앞에서 말했듯이, __new__는 Allocator의 역할로, 메모리를 할당한다. 하지만 위 예제에는 memory를 할당하는 코드가 보이지 않는다.
우선 아래 예제를 한번 더 살펴보자.
class Python :
#def __new__(cls):
# print("__new__")
def __init__(self):
print("__init__")
def say(self) :
print("hello python")
def main() :
print(1)
p = Python()
print(2)
p.say()
print(3)
if __name__ == '__main__' :
main()
__new__() 함수를 주석처리하니 실행이 잘되며, __init 함수는 p = Python()이 호출되는 시점에 출력이 되는 것을 알 수 있다.
다시 위에서 발생한 에러 문구를 살펴보자.
NoneType object에는 say하는 attribute가 없다는 의미다.
위와 같이 에러가 발생하는 이유에 대해 생각해보니...
우리가 일반적으로 python 코드를 작업할 때 __new__를 정의해서 사용하지 않는다.
이는 우리가 객체를 생성하려고 p = Python() 를 수행하면 system에서 자동으로 호출이 되는 듯 하다.
하지만, 개발자가 명시적으로 __new__() 함수를 오버라이드(재정의) 하면 system에서 자동으로 호출되지 않는 듯 하다. (class inheritance 에서 다시 살펴보자.)
위 코드의 경우, __new__ 함수에서 메모리를 할당하지 않고, print()만 호출했기 때문이다.
결국 __new__를 사용하려면, 좀 더 정확하게 __new__를 재정의 해주어야 한다.
그럼 메모리를 할당하는 코드를 추가하여 위 코드가 잘 동작하도록 해보자.
class Python :
def __new__(cls):
print("__new__")
return super().__new__(cls) # 메모리 할당 및 리턴
def __init__(self):
print("__init__")
def say(self) :
print("hello python")
def main() :
print(1)
p = Python()
print(2)
p.say()
print(3)
if __name__ == '__main__' :
main()
위 코드에서 __new__ 함수를 아래와 같이 수정하였다.
def __new__(cls):
print("__new__")
return super().__new__(cls)
super()는 상속에서 나오는 개념인데.. 나중에 살펴보도록 하자.
요약하면 부모 class의 __new__를 호출하고 그 결과를 return하는 코드를 추가하였다. ( 메모리를 할당하는 부분이 부모 class에서 수행됨 )
그럼 이제 앞에서 설명한대로, 객체가 생성될 때, __new__가 호출되고, 이후 __init__이 호출되는 것을 눈으로 확인하였다.
웬만하면 __new__는 system에서 자동으로 생성하도록 건들지 않는 것이 좋겠다.
[파이썬(Python)] #26 문자열 템플릿 (string template) (2) | 2021.09.27 |
---|---|
[파이썬(Python)] #25 상속 (0) | 2021.09.17 |
[파이썬(Python)] #14. 자료형 (딕셔너리(dict)) (0) | 2021.08.22 |
[파이썬(Python)] #10 모듈 생성 및 시작 (0) | 2021.08.22 |
[파이썬(Python)] #9. re 모듈, 정규식 표현 (0) | 2021.08.22 |