python 파이썬 get - __init __ ()가 __new __ () 뒤에 항상 호출되는 이유는 무엇입니까?





8 Answers

__new__ 는 정적 클래스 메소드이고, __init__ 은 인스턴스 메소드입니다. __new__ 는 먼저 인스턴스를 생성해야하므로 __init__ 은 인스턴스를 초기화 할 수 있습니다. __init__self 를 매개 변수로 취합니다. 인스턴스를 만들 때까지는 self 가 없습니다.

자, 여러분, 파이썬에서 싱글 톤 패턴 을 구현하려고합니다. 이를 수행 할 수있는 몇 가지 방법이 있습니다.

또한 Python 2.6 decorators 클래스 decorators 사용할 수 있습니다.

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...
set

난 그냥 내 수업 중 하나를 간소화하고 플라이급 디자인 패턴 과 같은 스타일로 몇 가지 기능을 도입했습니다.

그러나, 나는 __init__ 이 항상 __new__ 다음에 호출되는 이유에 대해 다소 혼란스러워합니다. 나는 이것을 기대하지 않았다. 아무도 왜 이런 일이 일어나고 어떻게 다른 기능을 구현할 수 있는지 말해 줄 수 있습니까? (구현을 __new__ 넣는 것 __new____new__ 된 것 같습니다.)

다음은 그 예입니다.

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return super(A, cls).__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self
        print ""

a1 = A()
a2 = A()
a3 = A()

출력 :

NEW
INIT

EXISTS
INIT

EXISTS
INIT

왜?




documentation 인용 :

일반적인 구현은 적절한 인수로 "super (currentclass, cls) .__ new __ (cls [, ...])"를 사용하여 수퍼 클래스의 __new __ () 메소드를 호출 한 다음 필요에 따라 새로 생성 된 인스턴스를 수정하여 클래스의 새 인스턴스를 만듭니다 그것을 반환하기 전에.

...

__new __ ()가 cls의 인스턴스를 반환하지 않으면 새 인스턴스의 __init __ () 메서드가 호출되지 않습니다.

__new __ ()은 주로 int 형, str 형, 또는 tuple과 같은 불변 형의 서브 클래스가 인스턴스 생성을 커스터마이즈 할 수 있도록하기위한 것입니다.




이 질문에 대한 간단한 대답은 __new__ 이 클래스와 동일한 유형의 값을 반환하면 __init__ 함수가 실행되고, 그렇지 않으면 __init__ 함수가 실행된다는 것입니다. 이 경우 코드는 cls 와 동일한 클래스 인 A._dict('key') 를 반환하므로 __init__ 가 실행됩니다.




class M(type):
    _dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print 'EXISTS'
            return cls._dict[key]
        else:
            print 'NEW'
            instance = super(M, cls).__call__(key)
            cls._dict[key] = instance
            return instance

class A(object):
    __metaclass__ = M

    def __init__(self, key):
        print 'INIT'
        self.key = key
        print

a1 = A('aaa')
a2 = A('bbb')
a3 = A('aaa')

출력 :

NEW
INIT

NEW
INIT

EXISTS

주의 부작용으로 M._dict 속성은 A 에서 A._dict 로 자동 액세스 할 수 있으므로 우연히 덮어 쓰지 않도록주의하십시오.




@AntonyHatchkins에 대한 대답으로, 메타 타입의 각 클래스에 대해 별도의 인스턴스 사전을 원할 것입니다. 즉, 모든 클래스에서 전역으로 설정하지 않고 클래스 객체를 해당 사전으로 초기화하기 위해 메타 클래스에 __init__ 메소드가 있어야합니다. .

class MetaQuasiSingleton(type):
    def __init__(cls, name, bases, attibutes):
        cls._dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print('EXISTS')
            instance = cls._dict[key]
        else:
            print('NEW')
            instance = super().__call__(key)
            cls._dict[key] = instance
        return instance

class A(metaclass=MetaQuasiSingleton):
    def __init__(self, key):
        print 'INIT'
        self.key = key
        print()

나는 원래 코드를 __init__ 업데이트하고 파이썬 3 표기법 (애트리뷰트로 대신 클래스 인자에서 super 와 metaclass를 사용하는 arg가없는 콜론)으로 구문을 변경했다.

어쨌든 중요한 점은 키가 발견되면 클래스 이니셜 라이저 ( __call__ 메서드)가 __new__ 또는 __init__ 실행하지 않는다는 것입니다. 이것은 __new__ 사용하는 것보다 훨씬 깔끔합니다. 기본 __init__ 단계를 건너 뛰려면 개체를 표시해야합니다.




그것에 깊이 파고 들기!

CPython에서 일반적인 클래스의 유형은 type 이며 기본 클래스는 Object (메타 클래스와 같은 다른 기본 클래스를 명시 적으로 정의하지 않는 한). 저수준 호출 시퀀스는 here 에서 찾을 수 here . 호출 된 첫 번째 메소드는 type_call 메소드는 tp_new 를 호출 한 다음 tp_init 를 호출합니다.

여기서 흥미로운 부분은 tp_newObject 의 메모리를 할당하는 tp_alloc ( PyType_GenericAlloc )을 수행하는 Object 의 새로운 메소드 ( object_new )를 호출한다는 것입니다. :)

이 시점에서 객체는 메모리에 생성되고 __init__ 메쏘드가 호출됩니다. 클래스에서 __init__ 이 구현되지 않으면 object_init 가 호출되고 아무 것도 수행하지 않습니다.

그런 다음 type_call 은 변수에 바인딩하는 객체를 반환합니다.




__init____new__ 다음에 호출되므로 하위 클래스에서 재정의 할 때 추가 된 코드가 여전히 호출됩니다.

이미 __new__ 가진 클래스를 하위 클래스로 만들려고한다면, 이것을 알지 못하는 누군가는 __init__ 을 적용하고 호출을 하위 클래스 __init__ 로 전달함으로써 시작할 수 있습니다. __new__ 다음에 __init__ 을 호출하는이 규칙은 예상대로 작업하는 데 도움이됩니다.

__init__ 여전히 슈퍼 클래스 __new__ 필요한 모든 매개 변수를 허용해야하지만 그렇게하지 않으면 일반적으로 명확한 런타임 오류가 발생합니다. 그리고 __new__ 는 아마도 *args 와 '** kw'를 명시 적으로 허용해야 확장이 OK라는 것을 분명히 알 수 있습니다.

원래 포스터가 설명한 동작 때문에 동일한 수준의 상속 수준에서 동일한 클래스에 __new____init__ 을 모두 포함하는 것은 일반적으로 나쁜 형식입니다.




간단한 이유는 인스턴스가 인스턴스를 만드는 데 사용되고 init 은 인스턴스를 초기화하는 데 사용된다는 것입니다. 초기화하기 전에 먼저 인스턴스를 만들어야합니다. 그래서 newinit 전에 호출되어야 합니다 .




Related