Die Frage, die ich stellen werde, scheint ein Duplikat von Pythons Verwendung von __new__ und __init__ zu sein. Trotzdem ist mir immer noch unklar, was genau der praktische Unterschied zwischen __new__
und __init__
ist.
Bevor Sie mir sagen, dass dies __new__
zum Erstellen von Objekten und __init__
zum Initialisieren von Objekten dient, lassen Sie mich klar sein: Ich verstehe das. Tatsächlich ist diese Unterscheidung für mich ganz natürlich, da ich Erfahrung in C ++ habe, wo wir eine neue Platzierung haben , die in ähnlicher Weise die Objektzuweisung von der Initialisierung trennt.
Das Python C API-Tutorial erklärt es folgendermaßen:
Das neue Mitglied ist für das Erstellen (im Gegensatz zum Initialisieren) von Objekten des Typs verantwortlich. Es wird in Python als
__new__()
Methode verfügbar gemacht. ... Ein Grund für die Implementierung einer neuen Methode besteht darin, die Anfangswerte der Instanzvariablen sicherzustellen .
Also, ja - ich verstehe , was es __new__
tut, aber trotzdem verstehe ich immer noch nicht, warum es in Python nützlich ist. Das angegebene Beispiel besagt, dass __new__
dies nützlich sein kann, wenn Sie "die Anfangswerte von Instanzvariablen sicherstellen" möchten. Ist das nicht genau das, was __init__
wir tun werden?
Im C API-Tutorial wird ein Beispiel gezeigt, in dem ein neuer Typ ("Noddy" genannt) erstellt und die __new__
Funktion des Typs definiert wird. Der Noddy-Typ enthält ein Zeichenfolgenelement mit dem Namen first
, und dieses Zeichenfolgenelement wird wie folgt mit einer leeren Zeichenfolge initialisiert:
static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
.....
self->first = PyString_FromString("");
if (self->first == NULL)
{
Py_DECREF(self);
return NULL;
}
.....
}
Beachten Sie, dass __new__
wir ohne die hier definierte Methode verwenden müssten PyType_GenericNew
, die einfach alle Instanzvariablenelemente auf NULL initialisiert. Der einzige Vorteil der __new__
Methode besteht darin, dass die Instanzvariable im Gegensatz zu NULL als leere Zeichenfolge beginnt. Aber warum ist das jemals nützlich, denn wenn wir sicherstellen wollten, dass unsere Instanzvariablen auf einen Standardwert initialisiert werden, hätten wir das einfach in der __init__
Methode tun können ?
quelle
__new__
ist kein Boilerplate, da sich das Boilerplate nie ändert. Manchmal müssen Sie diesen bestimmten Code durch etwas anderes ersetzen.super
Aufruf) und das Zurückgeben der Instanz sind notwendige Bestandteile jeder__new__
Implementierung und des "Boilerplate", auf das ich mich beziehe. Im Gegensatz dazupass
ist eine gültige Implementierung für__init__
- es ist überhaupt kein Verhalten erforderlich.Es gibt wahrscheinlich andere Verwendungszwecke,
__new__
aber es gibt einen wirklich offensichtlichen: Sie können einen unveränderlichen Typ nicht ohne Verwendung unterordnen__new__
. Angenommen, Sie möchten eine Unterklasse von Tupeln erstellen, die nur ganzzahlige Werte zwischen 0 und 0 enthalten kannsize
.Sie können einfach tun dies nicht mit
__init__
- wenn man versucht , zu ändern ,self
in__init__
der Interpreter würde, beschweren sich, dass Sie ein unveränderliches Objekt zu ändern versuchen.quelle
__new__
. Wir gehencls
explizit zu,__new__
weil, wie Sie hier lesen können ,__new__
immer ein Typ als erstes Argument erforderlich ist. Anschließend wird eine Instanz dieses Typs zurückgegeben. Wir geben also keine Instanz der Oberklasse zurück - wir geben eine Instanz von zurückcls
. In diesem Fall ist es genauso, als hätten wir gesagttuple.__new__(ModularTuple, tup)
.__new__()
kann Objekte anderer Typen als der Klasse zurückgeben, an die es gebunden ist.__init__()
Initialisiert nur eine vorhandene Instanz der Klasse.quelle
__init__
Methoden, die Code enthalten, der aussiehtself.__class__ = type(...)
. Dies führt dazu, dass das Objekt einer anderen Klasse angehört als die, von der Sie dachten, dass Sie sie erstellt haben. Ich kann es nicht so ändernint
wie Sie ... Ich erhalte eine Fehlermeldung über Heap-Typen oder ähnliches ... aber mein Beispiel für die Zuweisung zu einer dynamisch erstellten Klasse funktioniert.__init__()
angerufen wird. In der Antwort von lonetwin wird beispielsweise je nach zurückgegebenem Typ entwederTriangle.__init__()
oderSquare.__init__()
automatisch aufgerufen__new__()
. Nach dem, was Sie in Ihrer Antwort sagen (und ich habe dies an anderer Stelle gelesen), scheint es nicht so, als ob einer von beiden sein sollte, daShape.__new__()
keine Instanz voncls
(oder eine von einer Unterklasse davon) zurückgegeben wird.__init__()
Methoden in lonetwins Antwort werden aufgerufen, wenn die einzelnen Objekte instanziiert werden (dh wenn ihre__new__()
Methode zurückgegeben wird), nicht, wenn sieShape.__new__()
zurückgegeben werden.Shape.__init__()
(wenn es einen hätte) würde man nicht anrufen. Jetzt macht alles mehr Sinn ...:¬)
Keine vollständige Antwort, aber vielleicht etwas, das den Unterschied verdeutlicht.
__new__
wird immer aufgerufen, wenn ein Objekt erstellt werden muss. Es gibt Situationen, in denen__init__
nicht angerufen wird. Ein Beispiel ist, wenn Sie Objekte aus einer Pickle-Datei entfernen, werden sie zugewiesen (__new__
), aber nicht initialisiert (__init__
).quelle
__new__
Methode besteht darin , eine Instanz der Klasse zu erstellen (dies impliziert eine Speicherzuweisung) und diese zurückzugeben. Die Initialisierung ist ein separater Schritt und für den Benutzer normalerweise sichtbar. Bitte stellen Sie eine separate Frage, wenn Sie auf ein bestimmtes Problem stoßen.Ich möchte nur ein Wort über die hinzufügen Absicht (im Gegensatz zum Verhalten) ,
__new__
Versus zu definieren__init__
.Ich bin (unter anderem) auf diese Frage gestoßen, als ich versuchte zu verstehen, wie man eine Klassenfabrik am besten definiert. Ich erkannte, dass einer der Wege, auf denen
__new__
sich konzeptionell von der unterscheidet, darin__init__
besteht, dass der Nutzen__new__
genau das ist, was in der Frage angegeben wurde:In Anbetracht des angegebenen Szenarios kümmern wir uns um die Anfangswerte der Instanzvariablen, wenn die Instanz in Wirklichkeit eine Klasse selbst ist. Wenn wir also zur Laufzeit dynamisch ein Klassenobjekt erstellen und etwas Besonderes für die nachfolgenden Instanzen dieser Klasse definieren / steuern müssen, definieren wir diese Bedingungen / Eigenschaften in einer
__new__
Methode einer Metaklasse.Ich war darüber verwirrt, bis ich tatsächlich über die Anwendung des Konzepts nachdachte und nicht nur über die Bedeutung. Hier ist ein Beispiel, das hoffentlich den Unterschied deutlich machen würde:
Beachten Sie, dass dies nur ein dämonstartives Beispiel ist. Es gibt mehrere Möglichkeiten, eine Lösung zu erhalten, ohne auf einen Klassenfactory-Ansatz wie oben zurückzugreifen, und selbst wenn wir uns dafür entscheiden, die Lösung auf diese Weise zu implementieren, werden der Kürze halber einige Einschränkungen ausgelassen (z. B. explizite Deklaration der Metaklasse) )
Wenn Sie eine reguläre Klasse (auch als Nicht-Metaklasse bezeichnet) erstellen, ist
__new__
dies nur dann sinnvoll, wenn es sich um einen Sonderfall wie das veränderbare oder unveränderliche Szenario in der Antwort von ncoghlan handelt (was im Wesentlichen ein spezifischeres Beispiel für das Konzept der Definition ist Die Anfangswerte / Eigenschaften der Klasse / des Typs, über die erstellt wird__new__
, werden dann über initialisiert__init__
.quelle