Python (및 Python C API) : __new__ 및 __init__


126

내가 묻는 질문은 파이썬에서 __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__메소드 에서 그렇게 할 수 있었기 때문에 이것이 왜 유용한가 ?

답변:


137

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

__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__으로 필요한 경우 부모 클래스 메서드를 호출하는 것이 더 분명 합니다.


1
__new__상용 구판은 절대 변하지 않기 때문에 "보일러 판"이라고하는 코드 는 상용구가 아닙니다. 때로는 특정 코드를 다른 것으로 바꿔야합니다.
Miles Rout

13
인스턴스 (일반적으로 super호출)를 작성하거나 그렇지 않으면 인스턴스를 리턴하는 것은 __new__구현에 필요한 부분이며 , 내가 언급 한 "보일러 플레이트"입니다. 대조적으로, pass유효한 구현입니다 __init__-필요한 행동이 전혀 없습니다.
ncoghlan

37

아마도 다른 용도로 사용될 수도 __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__, 인터프리터는 불변의 객체를 수정하려는 불평 것입니다.


1
왜 슈퍼를 사용해야하는지 모르겠습니다. new가 왜 슈퍼 클래스의 인스턴스를 반환 해야 합니까? 또한 당신이 말한 것처럼 왜 cls를 명시 적으로 new에 전달해야 합니까? super (ModularTuple, cls)는 바인딩 된 메서드를 반환하지 않습니까?
Alcott

3
@Alcott,의 행동을 오해하고 있다고 생각합니다 __new__. 여기서 읽을 수 있듯이 항상 첫 번째 인수로 유형이 필요 cls하기 __new__때문에 명시 적으로 전달 합니다. 그런 다음 해당 유형의 인스턴스를 반환합니다. 따라서 우리는 수퍼 클래스의 인스턴스를 반환하지 않고-의 인스턴스를 반환합니다 . 이 경우, 우리가 말한 것과 동일 합니다. __new__ clstuple.__new__(ModularTuple, tup)
senderle

35

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

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

이것은 지금까지 가장 간단한 설명입니다.
Tarik

사실이 아닙니다. 나는이 __init__모양이 좋아하는 코드를 포함하는 방법 self.__class__ = type(...). 이로 인해 객체가 생성하려고 생각한 것과 다른 클래스가됩니다. 실제로 int당신이했던 것처럼 그것을 바꿀 수는 없습니다 ... 나는 힙 유형이나 무언가에 대해 오류가 발생하지만 ... 동적으로 생성 된 클래스에 할당하는 예는 작동합니다.
ArtOfWarfare

나도 언제 __init__()부름을 받았는지 혼란스러워 합니다. 예를 들어, lonetwin 's answer 에서 반환 되는 유형에 따라 Triangle.__init__()또는 Square.__init__()자동으로 호출됩니다 __new__(). 당신이 당신의 대답에서 말한 것 (그리고 나는 이것을 다른 곳에서 읽었습니다)에서, 인스턴스 중 하나 (또는 ​​하위 클래스 중 하나)를 반환 Shape.__new__() 하지 않기 때문에 둘 중 하나가되어야하는 것처럼 보이지 않습니다cls .
martineau

1
@martineau 다음 __init__()개별 개체를 인스턴스화 할 때 lonetwin의 대답 메소드가 호출되는 (즉, 때 자신의 __new__() 메소드가 리턴),하지 Shape.__new__()로 돌아갑니다.
Ignacio Vazquez-Abrams

아, 맞아요 Shape.__init__()(있는 경우). 이제 모든 것이 더 의미가 있습니다 ...:¬)
martineau

13

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

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


메모리를 할당하고 데이터를 초기화하려면 new 에서 init 을 호출 합니까? 인스턴스 초기화를 만들 때 new 가 존재하지 않는 이유는 무엇 입니까?
redpix_

2
__new__방법 의 역할은 클래스의 인스턴스 를 만들고 (메모리 할당을 의미 함) 반환하는 것입니다. 초기화는 별도의 단계이며 일반적으로 사용자에게 표시됩니다. 특정 문제가있는 경우 별도의 질문을하십시오.
Noufal Ibrahim

3

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__.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.