상세 컨텐츠

본문 제목

[파이썬(Python] #24 클래스(class) / 객체(instance) / __new__ / __init__

python

by 빨간눈동자 2021. 9. 17. 11:34

본문

반응형

오늘은 클래스 / 객체 / __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에서 자동으로 생성하도록 건들지 않는 것이 좋겠다. 

반응형

관련글 더보기