[python] __init __ ()이 왜 항상 __new __ () 뒤에 호출됩니까?

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

그러나 왜 __init__항상 호출 된지에 대해 약간 혼란 스럽습니다 __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

왜?



답변

__new__새 인스턴스 작성을 제어해야 할 때 사용하십시오 .

__init__새 인스턴스의 초기화를 제어해야 할 때 사용하십시오
.

__new__인스턴스 생성의 첫 단계입니다. 먼저 호출되며 클래스의 새 인스턴스를 반환합니다.

반대로,
__init__아무것도 반환하지 않습니다. 인스턴스를 만든 후에 만 ​​초기화해야합니다.

일반적으로 __new__str, int, unicode 또는 tuple과 같은 불변 유형을 서브 클래스 화하지 않는 한 재정의 할 필요가 없습니다 .

2008년 4월 게시물에서 : 때 사용하는 __new__대를 __init__? mail.python.org에서.

당신이하려는 일이 일반적으로 팩토리로 이루어지며 그것이 최선의 방법 이라는 것을 고려해야 합니다. 사용 __new__하는 것이 좋은 해결책은 아니므로 공장의 사용을 고려하십시오. 여기 좋은 공장 예가 있습니다.


답변

__new__정적 클래스 메서드이고 __init__인스턴스 메서드입니다.
__new__인스턴스를 먼저 만들어야하므로 __init__초기화 할 수 있습니다. 주 __init__소요 self매개 변수로. 인스턴스를 만들 때까지는 없습니다 self.

이제 파이썬에서 싱글 톤 패턴 을 구현하려고 노력하고 있습니다. 몇 가지 방법이 있습니다.

또한 Python 2.6부터 클래스 데코레이터를 사용할 수 있습니다 .

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

@singleton
class MyClass:
  ...


답변

가장 잘 알려진 OO 언어에서와 같은 표현식 SomeClass(arg1, arg2)은 새 인스턴스를 할당하고 인스턴스의 속성을 초기화 한 다음 반환합니다.

가장 잘 알려진 OO 언어에서 “인스턴스 속성 초기화”부분 은 기본적으로 새 인스턴스에서 작동하는 코드 블록 (생성자 표현식에 제공된 인수를 사용하여) 인 생성자 를 정의하여 각 클래스에 대해 사용자 정의 할 수 있습니다. )를 사용하여 원하는 초기 조건을 설정하십시오. 파이썬에서 이것은 클래스의 __init__메소드에 해당합니다 .

파이썬 __new__은 “새 인스턴스 할당”부분의 비슷한 클래스 별 사용자 정의에 지나지 않습니다. 물론 새 인스턴스를 할당하는 대신 기존 인스턴스를 반환하는 등의 비정상적인 작업을 수행 할 수 있습니다. 파이썬에서 우리는이 부분이 반드시 할당과 관련이 있다고 생각해서는 안됩니다. 우리가 필요로하는 것은 __new__어딘가에서 적합한 인스턴스를 만들어 내는 것입니다.

그러나 그것은 여전히 ​​작업의 절반에 불과하며, 파이썬 시스템이 때로는 작업의 다른 절반을 실행하고 싶을 때가 있다는 것을 알 __init__수있는 방법이 없습니다. 당신이 그 행동을 원한다면, 당신은 그렇게 분명하게 말해야합니다.

종종 리팩토링하여 필요 __new__하거나 필요하지 않거나 이미 초기화 된 객체에서 다르게 동작 __new__하도록 할 수 __init__있습니다. 당신이 정말로, 파이썬은 실제로 수 있습니까하려는 경우 그이 있도록하지만, “작업”을 다시 정의 할 SomeClass(arg1, arg2)필요는 호출하지 않습니다 __new__다음에 __init__. 이렇게하려면 메타 클래스를 작성하고 해당 __call__메소드를 정의해야 합니다.

메타 클래스는 클래스의 클래스 일뿐입니다. 그리고 클래스의 __call__메소드는 클래스의 인스턴스를 호출 할 때 발생하는 일을 제어합니다. 따라서 metaclass__call__메소드는 클래스를 호출 할 때 발생하는 상황을 제어합니다. 즉 , 인스턴스 생성 메커니즘을 처음부터 끝까지 재정의 할 수 있습니다 . 싱글 톤 패턴과 같은 완전히 비표준 인스턴스 생성 프로세스를 가장 우아하게 구현할 수있는 수준입니다. 사실, 코드의 10 개 라인 당신이 구현할 수있는 Singleton다음도 futz에 당신을 필요로하지 않는 메타 클래스를 __new__ 전혀 하고 설정할 수 있는 간단하게 추가하여 싱글로, 그렇지 않으면 정상 수업을 __metaclass__ = Singleton!

class Singleton(type):
    def __init__(self, *args, **kwargs):
        super(Singleton, self).__init__(*args, **kwargs)
        self.__instance = None
    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance

그러나 이것은 아마도이 상황에서 보장되는 것보다 더 깊은 마법 일 것입니다!


답변

설명서 를 인용하려면 :

일반적인 구현에서는 적절한 인수와 함께 “super (currentclass, cls) .__ new __ (cls [, …])”를 사용하여 수퍼 클래스의 __new __ () 메서드를 호출 한 다음 필요에 따라 새로 만든 인스턴스를 수정하여 클래스의 새 인스턴스를 만듭니다. 그것을 반환하기 전에.

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

__new __ ()는 주로 변경 불가능한 유형의 하위 클래스 (int, str 또는 tuple과 같은)가 인스턴스 작성을 사용자 정의 할 수 있도록하기위한 것입니다.


답변

이 질문은 상당히 오래되었지만 비슷한 문제가 있음을 알고 있습니다. 다음은 내가 원하는 것을했습니다.

class Agent(object):
    _agents = dict()

    def __new__(cls, *p):
        number = p[0]
        if not number in cls._agents:
            cls._agents[number] = object.__new__(cls)
        return cls._agents[number]

    def __init__(self, number):
        self.number = number

    def __eq__(self, rhs):
        return self.number == rhs.number

Agent("a") is Agent("a") == True

이 페이지를 리소스 http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html 로 사용했습니다.


답변

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


답변

__new__같은 클래스의 인스턴스를 반환 하면 __init__나중에 반환 된 객체에서 실행됩니다. 즉, 실행 __new__을 막기 위해 사용할 수 없습니다 __init__. 에서 이전에 생성 된 객체를 반환하더라도 반복 __new__해서 두 번 초기화됩니다 (트리플 등) __init__.

여기에 vartec 답변을 확장하고 수정하는 Singleton 패턴에 대한 일반적인 접근 방식이 있습니다.

def SingletonClass(cls):
    class Single(cls):
        __doc__ = cls.__doc__
        _initialized = False
        _instance = None

        def __new__(cls, *args, **kwargs):
            if not cls._instance:
                cls._instance = super(Single, cls).__new__(cls, *args, **kwargs)
            return cls._instance

        def __init__(self, *args, **kwargs):
            if self._initialized:
                return
            super(Single, self).__init__(*args, **kwargs)
            self.__class__._initialized = True  # Its crucial to set this variable on the class!
    return Single

전체 이야기는 여기에 있습니다 .

실제로 관련된 또 다른 접근법 __new__은 클래스 메소드를 사용 하는 것 입니다.

class Singleton(object):
    __initialized = False

    def __new__(cls, *args, **kwargs):
        if not cls.__initialized:
            cls.__init__(*args, **kwargs)
            cls.__initialized = True
        return cls


class MyClass(Singleton):
    @classmethod
    def __init__(cls, x, y):
        print "init is here"

    @classmethod
    def do(cls):
        print "doing stuff"

이 방법을 @classmethod사용하면의 실제 인스턴스를 사용하지 않으므로 모든 메소드를로 장식해야합니다 MyClass.