내가 묻는 질문은 파이썬에서 __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__
.