[python] 왜 __ ()는 항상 __ __new 후에 호출 __init한다 ()?


7 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:
  ...
Question

난 그냥 내 클래스 중 하나를 간소화하기 위해 노력하고있어와 같은 스타일의 일부 기능이 도입 플라이급 디자인 패턴을 .

그러나, 나는 __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

왜?




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

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

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

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




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




documentation 인용 :

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

...

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

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




__new__는 클래스의 새로운 빈 인스턴스를 반환해야합니다. 그런 다음 __init__을 호출하여 해당 인스턴스를 초기화합니다. __new__의 "NEW"케이스에서는 __init__을 호출하지 않으므로 호출됩니다. __new__ 호출하는 코드는 특정 인스턴스에서 __init__이 호출되었는지 여부를 추적하지 않습니다. 왜냐하면 여기서 매우 이상한 일을하고 있기 때문입니다.

__init__ 함수의 객체에 속성을 추가하여 객체가 초기화되었음을 나타낼 수 있습니다. 그 속성의 존재가 __init__의 첫 번째 것임을 확인하고 더 이상 진행하지 마십시오.




@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__ 단계를 건너 뛰려면 개체를 표시해야합니다.




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




init 은 전통적인 OO 언어에서 간단한 생성자로보아야한다. 예를 들어, Java 또는 C ++에 익숙한 경우, 생성자는 암시 적으로 자체 인스턴스에 대한 포인터를 전달받습니다. Java의 경우 "this"변수입니다. Java 용으로 생성 된 바이트 코드를 검사 할 경우 두 번의 호출이 발생합니다. 첫 번째 호출은 "새로운"메소드에 대한 호출이고, 다음 호출은 init 메소드 (사용자 정의 생성자에 대한 실제 호출)에 대한 호출입니다. 이 두 단계 프로세스를 통해 해당 인스턴스의 또 다른 메소드 인 클래스의 생성자 메소드를 호출하기 전에 실제 인스턴스를 작성할 수 있습니다.

이제 Python의 경우 new 는 사용자가 액세스 할 수있는 추가 기능입니다. Java는 유형이 정해져있어 유연성을 제공하지 않습니다. 언어가 그 기능을 제공했다면 new 의 구현자는 인스턴스를 반환하기 전에 그 메소드에서 많은 것을 할 수 있습니다. 어떤 경우에는 관련이없는 객체의 완전히 새로운 인스턴스를 생성하는 것을 포함합니다. 그리고,이 접근법은 특히 Python의 경우 불변의 타입을 위해서 잘 작동합니다.




Related