[python] Python (및 Python C API) : __new__ 및 __init__

내가 묻는 질문은 파이썬에서 __new__ 및 __init__을 사용 하는 것과 중복되는 것 같습니다 . 그러나 그럼에도 불구 __new__하고와 의 실제 차이점이 정확히 무엇인지는 여전히 명확하지 않습니다 __init__.

__new__객체 생성과 __init__객체 초기화를 위해 서두르 기 전에 분명히하자 . 사실, 우리가 배치를 새로 배치 한 C ++에서 경험을 가지고 있기 때문에 객체 구별과 초기화를 구분하기 때문에 그 구별은 나에게 자연 스럽습니다 .

파이썬 C의 API 튜토리얼은 이런 식으로 설명 :

새 멤버는 유형의 객체를 초기화하는 것이 아니라 생성하는 역할을합니다. 그것은 __new__()방법 으로 파이썬에서 노출됩니다 . …
새로운 메소드를 구현하는 한 가지 이유는 인스턴스 변수의 초기 값을 보장하는 것 입니다.

그래서, 그렇습니다-나는 무엇을 얻지__new__ 만, 그럼에도 불구하고 여전히 파이썬에서 그것이 왜 유용한 지 이해하지 못합니다. 주어진 예는 __new__“인스턴스 변수의 초기 값을 보장”하려는 경우 유용 할 수 있다고 말합니다 . 글쎄, 그게 정확히 무엇 __init__입니까?

C API 학습서에서는 새 유형 ( “Noddy”)이 작성되고 유형의 __new__기능이 정의 된 예제가 표시 됩니다. Noddy 타입은이라는 문자열 멤버를 포함하고 first있으며,이 문자열 멤버는 다음과 같이 빈 문자열로 초기화됩니다 :

static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    .....

    self->first = PyString_FromString("");
    if (self->first == NULL)
    {
       Py_DECREF(self);
       return NULL;
    }

    .....
}

__new__여기에 정의 된 메소드 가 없으면 PyType_GenericNew모든 인스턴스 변수 멤버를 NULL로 초기화하는 을 사용해야 합니다. 따라서이 __new__메소드 의 유일한 이점은 인스턴스 변수가 NULL이 아닌 빈 문자열로 시작한다는 것입니다. 그러나 인스턴스 변수가 기본값으로 초기화되도록 신경 쓰면 __init__메소드 에서 그렇게 할 수 있었기 때문에 이것이 왜 유용한가 ?



답변

차이는 주로 변경 가능한 유형과 변경 불가능한 유형에서 발생합니다.

__new__형식 을 첫 번째 인수로 받아들이고 일반적으로 해당 형식의 새 인스턴스를 반환합니다. 따라서 가변 및 불변 유형 모두에 사용하기에 적합합니다.

__init__인스턴스 를 첫 번째 인수로 받아들이고 해당 인스턴스 의 속성을 수정합니다. 이것은 불완전한 유형에는 부적합합니다 obj.__init__(*args). 호출 한 후 생성 후 수정할 수 있기 때문입니다 .

tuple및 의 동작을 비교하십시오 list.

>>> x = (1, 2)
>>> x
(1, 2)
>>> x.__init__([3, 4])
>>> x # tuple.__init__ does nothing
(1, 2)
>>> y = [1, 2]
>>> y
[1, 2]
>>> y.__init__([3, 4])
>>> y # list.__init__ reinitialises the object
[3, 4]

그것들이 왜 분리되어 있는지에 관해서 (단순한 역사적 이유는 제외하고) : __new__메소드는 올바른 초기 단계 (초기 객체 생성 후 마지막에 객체를 반환하는 것을 기억하기 위해) 많은 상용구가 필요합니다. __init__대조적으로, 메소드는 설정해야 할 속성을 설정하기 때문에 매우 간단합니다.

이외에도에서 __init__쓰기에 더 쉽게되는 방법, 불변의 구분 대 가변 위에서 언급, 분리는 부모 클래스를 호출하기 위해 악용 될 수있는 __init__서브 클래스는 어떠한 절대적으로 필요한 인스턴스 불변을 설정하여 선택 __new__. 이것은 일반적으로 모호한 관행입니다. 일반적 __init__으로 필요한 경우 부모 클래스 메서드를 호출하는 것이 더 분명 합니다.


답변

아마도 다른 용도로 사용될 수도 __new__있지만 실제로 명백한 용도가 있습니다 __new__. 를 사용하지 않고 불변 유형을 서브 클래 싱 할 수 없습니다 . 예를 들어, 0과 사이의 정수 값만 포함 할 수있는 튜플의 서브 클래스를 작성하려한다고 가정하십시오 size.

class ModularTuple(tuple):
    def __new__(cls, tup, size=100):
        tup = (int(x) % size for x in tup)
        return super(ModularTuple, cls).__new__(cls, tup)

당신은 단순히 함께 할 수 없습니다 __init__– 수정하려고하는 경우 self__init__, 인터프리터는 불변의 객체를 수정하려는 불평 것입니다.


답변

__new__()바인딩 된 클래스 이외의 유형의 객체를 반환 할 수 있습니다. __init__()클래스의 기존 인스턴스 만 초기화합니다.

>>> class C(object):
...   def __new__(cls):
...     return 5
...
>>> c = C()
>>> print type(c)
<type 'int'>
>>> print c
5


답변

완전한 대답은 아니지만 아마도 차이를 보여주는 것일 수 있습니다.

__new__객체를 만들어야 할 때 항상 호출됩니다. __init__호출되지 않는 상황이 있습니다 . 예를 들어 피클 파일에서 객체를 피클 링 해제하면 할당 ( __new__)되지만 초기화되지는 않습니다 ( __init__).


답변

vs 를 정의 하려는 의도 에 대해 단어를 추가하고 싶습니다 .__new____init__

클래스 팩토리를 정의하는 가장 좋은 방법을 이해하려고 할 때이 질문을 겪었습니다. 나는 __new__개념적으로 다른 방법 중 하나 는 질문 __init__의 이점 __new__이 정확히 다음과 같다는 사실이라는 것을 깨달았습니다 .

따라서 __new__ 메소드의 유일한 이점은 인스턴스 변수가 NULL이 아니라 빈 문자열로 시작한다는 것입니다. 그러나 인스턴스 변수가 기본값으로 초기화되도록 신경 쓰면 __init__ 메소드에서 방금 할 수 있었기 때문에 이것이 왜 유용한가?

명시된 시나리오를 고려할 때 인스턴스 가 실제로 클래스 자체 인 경우 인스턴스 변수의 초기 값에주의를 기울 입니다. 따라서 런타임에 클래스 객체를 동적으로 생성하고이 클래스의 후속 인스턴스에 대해 특별한 것을 정의 / 제어해야하는 __new__경우 메타 클래스 의 메소드 에서 이러한 조건 / 속성을 정의합니다 .

개념의 의미가 아닌 개념의 적용에 대해 실제로 생각할 때까지 혼란 스러웠습니다. 다음은 희망적으로 차이를 분명히하는 예입니다.

a = Shape(sides=3, base=2, height=12)
b = Shape(sides=4, length=2)
print(a.area())
print(b.area())

# I want `a` and `b` to be an instances of either of 'Square' or 'Triangle'
# depending on number of sides and also the `.area()` method to do the right
# thing. How do I do that without creating a Shape class with all the
# methods having a bunch of `if`s ? Here is one possibility

class Shape:
    def __new__(cls, sides, *args, **kwargs):
        if sides == 3:
            return Triangle(*args, **kwargs)
        else:
            return Square(*args, **kwargs)

class Triangle:
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return (self.base * self.height) / 2

class Square:
    def __init__(self, length):
        self.length = length

    def area(self):
        return self.length*self.length

이것은 단지 예시적인 예일뿐입니다. 위와 같은 클래스 팩토리 접근 방식에 의존하지 않고 솔루션을 얻는 방법에는 여러 가지가 있으며, 이러한 방식으로 솔루션을 구현하기로 선택하더라도 간결성을 위해 몇 가지주의 사항이 남아 있습니다 (예 : 메타 클래스를 명시 적으로 선언) )

정규 클래스 (일명 비 메타 클래스)를 생성하는 __new__경우 ncoghlan의 답변 답변 에서 변경 가능 대 변경 불가능 시나리오와 같은 특별한 경우가 아니라면 실제로 의미가 없습니다 (이것은 본질적으로 정의 개념의 더 구체적인 예입니다) 를 통해 생성 된 클래스 / 유형의 초기 값 / 속성은을 통해 __new__초기화됩니다 __init__.


답변