복붙노트

[PYTHON] 클래스 '__new__ 메소드를 팩토리로 사용 : __init__이 두 번 호출 됨

PYTHON

클래스 '__new__ 메소드를 팩토리로 사용 : __init__이 두 번 호출 됨

나는 파이썬에서 이상한 버그를 만났습니다. __new__ 메소드를 팩토리로 사용하면 인스턴스화 된 클래스의 __init__ 메소드가 두 번 호출됩니다.

이 아이디어는 원래 mother 클래스의 __new__ 메소드를 사용하여 전달 된 매개 변수에 따라 자식 중 하나의 특정 인스턴스를 반환하고 클래스 외부의 팩토리 함수를 선언하지 않아도되었습니다.

팩토리 함수를 사용하는 것이 여기서 가장 좋은 디자인 패턴이 될 것이지만 프로젝트의이 시점에서 디자인 패턴을 변경하는 것은 비용이 많이 듭니다. 그러므로 내 질문은 : __init__에 대한 이중 호출을 피하고 그러한 스키마에서 __init__에 대한 단일 호출 만 얻는 방법이 있습니까?

class Shape(object):
    def __new__(cls, desc):
        if cls is Shape:
            if desc == 'big':   return Rectangle(desc)
            if desc == 'small': return Triangle(desc)
        else:
            return super(Shape, cls).__new__(cls, desc)

    def __init__(self, desc):
        print "init called"
        self.desc = desc

class Triangle(Shape):
    @property
    def number_of_edges(self): return 3

class Rectangle(Shape):
    @property
    def number_of_edges(self): return 4

instance = Shape('small')
print instance.number_of_edges

>>> init called
>>> init called
>>> 3

어떤 도움이라도 대단히 감사합니다.

해결법

  1. ==============================

    1.객체를 생성 할 때 Python은 __new__ 메소드를 호출하여 객체를 생성 한 다음 반환 된 객체에서 __init__을 호출합니다. Triangle ()을 호출하여 __new__ 내부에서 객체를 만들면 __new__ 및 __init__이 추가로 호출됩니다.

    객체를 생성 할 때 Python은 __new__ 메소드를 호출하여 객체를 생성 한 다음 반환 된 객체에서 __init__을 호출합니다. Triangle ()을 호출하여 __new__ 내부에서 객체를 만들면 __new__ 및 __init__이 추가로 호출됩니다.

    당신이해야 할 일은 :

    class Shape(object):
        def __new__(cls, desc):
            if cls is Shape:
                if desc == 'big':   return super(Shape, cls).__new__(Rectangle)
                if desc == 'small': return super(Shape, cls).__new__(Triangle)
            else:
                return super(Shape, cls).__new__(cls, desc)
    

    __init__에 대한 호출을 트리거하지 않고 Rectangle 또는 Triangle을 생성 한 다음 __init__은 한 번만 호출됩니다.

    수퍼 워크가 어떻게 작동하는지에 대한 @ Adrian의 질문에 답하기 위해 편집하십시오.

    super (Shape, cls)는 cls .__ mro__을 검색하여 Shape를 찾은 다음 나머지 시퀀스를 검색하여 속성을 찾습니다.

    삼각형 .__ mro__은 (삼각형, 도형, 객체)이며 Rectangle .__ mro__은 (Rectangle, Shape, object)이고 Shape .__ mro__은 그냥 (Shape, object)입니다. super (Shape, cls)를 호출 할 때, Mroquence의 모든 것을 Shape를 포함하여 무시합니다. 그래서 남은 유일한 것은 단일 엘리먼트 튜플 (object)이며 원하는 속성을 찾는 데 사용됩니다.

    다이아몬드 상속을 받으면 이것은 더 복잡해질 것입니다 :

    class A(object): pass
    class B(A): pass
    class C(A): pass
    class D(B,C): pass
    

    이제 B의 메소드는 super (B, cls)를 사용할 수 있고 B 인스턴스 인 경우 (A, 객체)를 검색하지만 D 인스턴스가있는 경우 B에서 동일한 호출이 (C, A, 객체)를 검색합니다. D .__ mro__는 (B, C, A, object)입니다.

    따라서이 특별한 경우에 셰이프의 구성 동작을 수정하는 새로운 mixin 클래스를 정의 할 수 있으며 기존 삼각형과 사각형을 상속 받지만 다르게 구성되는 특수화 된 삼각형과 사각형을 가질 수 있습니다.

  2. ==============================

    2.내 질문을 게시 한 후, 해킹과 같은 문제를 해결할 수있는 방법을 찾은 솔루션을 계속 찾았습니다. Duncan의 솔루션보다 열등하지만 나는 아무 것도 언급하지 않는 것이 흥미로울 수 있다고 생각했습니다. 낫 (Shapeclass)

    내 질문을 게시 한 후, 해킹과 같은 문제를 해결할 수있는 방법을 찾은 솔루션을 계속 찾았습니다. Duncan의 솔루션보다 열등하지만 나는 아무 것도 언급하지 않는 것이 흥미로울 수 있다고 생각했습니다. 낫 (Shapeclass)

    class ShapeFactory(type):
        def __call__(cls, desc):
            if cls is Shape:
                if desc == 'big':   return Rectangle(desc)
                if desc == 'small': return Triangle(desc)
            return type.__call__(cls, desc)
    
    class Shape(object):
        __metaclass__ = ShapeFactory 
        def __init__(self, desc):
            print "init called"
            self.desc = desc
    
  3. ==============================

    3.필자가 설치 한 파이썬 인터프리터 중 하나에서이 동작을 실제로 재현 할 수는 없습니다. 따라서 이것은 추측의 일부입니다. 하나...

    필자가 설치 한 파이썬 인터프리터 중 하나에서이 동작을 실제로 재현 할 수는 없습니다. 따라서 이것은 추측의 일부입니다. 하나...

    __init__은 두 개의 객체, 즉 원래의 Shape 객체와 그 하위 객체 중 하나를 초기화하기 때문에 두 번 호출됩니다. __init__을 변경하여 초기화되는 객체의 클래스도 인쇄하도록하면이를 볼 수 있습니다.

    print type(self), "init called"
    

    __new __ ()에서 참조를 반환하지 않으므로 원래 Shape은 삭제되므로 무해한 것입니다.

    함수를 호출하는 것은 클래스를 인스턴스화하는 것과 문법적으로 동일하므로 다른 것을 변경하지 않고이 함수를 함수로 변경할 수 있습니다. 그렇게하는 것이 좋습니다. 나는 네가 꺼리는 것을 이해하지 못한다.

  4. from https://stackoverflow.com/questions/5953759/using-a-class-new-method-as-a-factory-init-gets-called-twice by cc-by-sa and MIT license