Ich versuche nur, eine meiner Klassen zu optimieren, und habe einige Funktionen im gleichen Stil wie das Flyweight-Designmuster eingeführt .
Ich bin jedoch etwas verwirrt darüber, warum __init__
immer danach gerufen wird __new__
. Ich habe das nicht erwartet. Kann mir jemand sagen, warum dies geschieht und wie ich diese Funktionalität ansonsten implementieren kann? (Abgesehen davon, dass die Implementierung in die integriert wird, __new__
die sich ziemlich hackig anfühlt.)
Hier ist ein Beispiel:
class A(object):
_dict = dict()
def __new__(cls):
if 'key' in A._dict:
print "EXISTS"
return A._dict['key']
else:
print "NEW"
return super(A, cls).__new__(cls)
def __init__(self):
print "INIT"
A._dict['key'] = self
print ""
a1 = A()
a2 = A()
a3 = A()
Ausgänge:
NEW
INIT
EXISTS
INIT
EXISTS
INIT
Warum?
Antworten:
Ab April 2008 Beitrag: Wann verwendet
__new__
vs.__init__
? auf mail.python.org.Sie sollten bedenken, dass das, was Sie versuchen, normalerweise mit einer Fabrik gemacht wird, und das ist der beste Weg, dies zu tun. Die Verwendung
__new__
ist keine gute saubere Lösung. Bitte berücksichtigen Sie die Verwendung einer Fabrik. Hier haben Sie ein gutes Fabrikbeispiel .quelle
__new__
in einer Factory-Klasse, die ziemlich sauber geworden ist. Vielen Dank für Ihre Eingabe.__new__
streng auf die angegebenen Fälle beschränkt sein sollte. Ich fand es sehr nützlich für die Implementierung erweiterbarer generischer Klassenfabriken - siehe meine Antwort auf die Frage Unsachgemäße Verwendung__new__
zum Generieren von Klassen in Python? für ein Beispiel dafür.__new__
ist eine statische Klassenmethode, während__init__
es sich um eine Instanzmethode handelt.__new__
muss zuerst die Instanz erstellen,__init__
kann sie also initialisieren. Beachten Sie, dass__init__
nimmtself
als Parameter. Bis Sie eine Instanz erstellen, gibt es keineself
.Nun, ich verstehe, dass Sie versuchen, ein Singleton-Muster in Python zu implementieren . Dafür gibt es einige Möglichkeiten.
Auch, wie von Python 2.6, können Sie Klasse verwenden Dekorateure .
quelle
MyClass
an eine Funktion gebunden wird, die Instanzen der ursprünglichen Klasse zurückgibt. Aber es gibt jetzt überhaupt keine Möglichkeit, auf die ursprüngliche Klasse zu verweisen, undMyClass
eine Funktion zu unterbrechenisinstance
/ zuissubclass
prüfen, direkt auf Klassenattribute / -methoden zuzugreifenMyClass.something
, die Klasse insuper
Aufrufen zu benennen usw. usw. Ob dies ein Problem ist oder nicht, hängt von der ab Klasse, auf die Sie es anwenden (und den Rest des Programms).In den meisten bekannten OO-Sprachen weist ein Ausdruck wie
SomeClass(arg1, arg2)
eine neue Instanz zu, initialisiert die Attribute der Instanz und gibt sie dann zurück.In den meisten bekannten OO-Sprachen kann der Teil "Attribute der Instanz initialisieren" für jede Klasse angepasst werden, indem ein Konstruktor definiert wird. Dies ist im Grunde nur ein Codeblock, der auf der neuen Instanz ausgeführt wird (unter Verwendung der Argumente, die für den Konstruktorausdruck bereitgestellt werden ), um die gewünschten Anfangsbedingungen festzulegen. In Python entspricht dies der
__init__
Methode der Klasse .Pythons
__new__
ist nicht mehr und nicht weniger als eine ähnliche Anpassung pro Klasse des Teils "Neue Instanz zuweisen". Auf diese Weise können Sie natürlich ungewöhnliche Dinge tun, z. B. eine vorhandene Instanz zurückgeben, anstatt eine neue zuzuweisen. In Python sollten wir uns diesen Teil also nicht unbedingt als notwendige Zuordnung vorstellen. Alles, was wir brauchen, ist, dass wir__new__
irgendwo eine geeignete Instanz finden.Aber es ist immer noch nur die Hälfte des Jobs, und das Python-System kann nicht wissen, dass Sie manchmal die andere Hälfte des Jobs (
__init__
) danach ausführen möchten und manchmal nicht. Wenn Sie dieses Verhalten wollen, müssen Sie dies explizit sagen.Oft können Sie eine Umgestaltung vornehmen, die Sie nur benötigen
__new__
oder nicht benötigen__new__
oder die__init__
sich bei einem bereits initialisierten Objekt anders verhält. Wenn Sie dies jedoch wirklich möchten, können Sie mit Python "den Job" neu definieren, sodassSomeClass(arg1, arg2)
nicht unbedingt ein Aufruf__new__
gefolgt von " aufrufen " erforderlich ist__init__
. Dazu müssen Sie eine Metaklasse erstellen und deren__call__
Methode definieren .Eine Metaklasse ist nur die Klasse einer Klasse. Die
__call__
Methode einer Klasse steuert, was passiert, wenn Sie Instanzen der Klasse aufrufen. So ein Metaklasse "__call__
Methode steuert , was passiert , wenn Sie eine Klasse nennen; Das heißt, Sie können den Mechanismus zur Instanzerstellung von Anfang bis Ende neu definieren . Auf dieser Ebene können Sie einen völlig nicht standardmäßigen Instanzerstellungsprozess wie das Singleton-Muster am elegantesten implementieren. In der Tat, mit weniger als 10 Zeilen Code können Sie implementierenSingleton
Metaklasse , dass dann auch Sie nicht mit zu futz benötigen__new__
überhaupt , und drehen kann jede sonst normale Klasse in ein Singleton durch einfaches Hinzufügen__metaclass__ = Singleton
!Dies ist jedoch wahrscheinlich eine tiefere Magie, als dies für diese Situation wirklich gerechtfertigt ist!
quelle
So zitieren Sie die Dokumentation :
quelle
If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked.
Das ist ein wichtiger Punkt. Wenn Sie eine andere Instanz zurückgeben, wird das Original__init__
niemals aufgerufen.__new__
würde die Methode einen Fehler auslösen, wenn das zurückgegebene Objekt von auf seine Klasse überprüft wird, anstatt zuzulassen, dass die fehlgeschlagene Prüfung stillschweigend übergeben wird, da ich nicht verstehe, warum Sie jemals etwas anderes als ein Objekt der Klasse zurückgeben möchten .Mir ist klar, dass diese Frage ziemlich alt ist, aber ich hatte ein ähnliches Problem. Folgendes hat getan, was ich wollte:
Ich habe diese Seite als Ressource verwendet: http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html
quelle
Ich denke, die einfache Antwort auf diese Frage lautet: Wenn
__new__
ein Wert zurückgegeben wird, der vom Typ der Klasse ist, wird die__init__
Funktion ausgeführt, andernfalls nicht. In diesem Fall gibt Ihr CodeA._dict('key')
die gleiche Klasse zurück wiecls
,__init__
wird also ausgeführt.quelle
Wenn eine
__new__
Instanz derselben Klasse zurückgegeben wird,__init__
wird sie anschließend für das zurückgegebene Objekt ausgeführt. Dh Sie können NICHT verwenden__new__
, um zu verhindern,__init__
dass ausgeführt wird. Selbst wenn Sie ein zuvor erstelltes Objekt von zurückgeben__new__
, wird es immer wieder doppelt (dreifach usw.) initialisiert__init__
.Hier ist der generische Ansatz für das Singleton-Muster, der die obige vartec-Antwort erweitert und behebt:
Die ganze Geschichte ist hier .
Ein anderer Ansatz, der tatsächlich beinhaltet,
__new__
ist die Verwendung von Klassenmethoden:Bitte beachten Sie, dass Sie bei diesem Ansatz ALLE Ihre Methoden dekorieren müssen
@classmethod
, da Sie niemals eine echte Instanz von verwenden werdenMyClass
.quelle
Verweis auf dieses Dokument :
In Bezug auf das, was Sie erreichen möchten, finden Sie in derselben Dokumentation auch Informationen zum Singleton-Muster
Sie können diese Implementierung auch aus PEP 318 mit einem Dekorator verwenden
quelle
init
from zu nennen,__new__
klingt wirklich hackig. Dafür sind Metaklassen da.Ausgänge:
NB Als Nebeneffekt
M._dict
Eigenschaft wird automatisch zugänglichA
alsA._dict
so kümmern sie nicht zufällig zu überschreiben.quelle
__init__
Methodecls._dict = {}
. Sie möchten wahrscheinlich nicht, dass ein Wörterbuch von allen Klassen dieses Metatyps gemeinsam genutzt wird (aber +1 für die Idee).__new__ sollte eine neue, leere Instanz einer Klasse zurückgeben. __init__ wird dann aufgerufen, um diese Instanz zu initialisieren. Sie rufen __init__ im "NEUEN" Fall von __new__ nicht an, also wird es für Sie aufgerufen. Der aufrufende Code verfolgt
__new__
nicht, ob __init__ für eine bestimmte Instanz aufgerufen wurde oder nicht, und sollte es auch nicht, da Sie hier etwas sehr Ungewöhnliches tun.Sie können dem Objekt in der Funktion __init__ ein Attribut hinzufügen, um anzuzeigen, dass es initialisiert wurde. Überprüfen Sie als erstes in __init__, ob dieses Attribut vorhanden ist, und fahren Sie nicht fort, wenn dies der Fall war.
quelle
Bei einer Aktualisierung der Antwort von @AntonyHatchkins möchten Sie wahrscheinlich ein separates Instanzwörterbuch für jede Klasse des Metatyps. Dies bedeutet, dass Sie eine
__init__
Methode in der Metaklasse haben sollten, um Ihr Klassenobjekt mit diesem Wörterbuch zu initialisieren, anstatt es für alle Klassen global zu machen.Ich habe den ursprünglichen Code mit einer
__init__
Methode aktualisiert und die Syntax in die Python 3-Notation geändert (Aufruf ohnesuper
Argumente und Metaklasse in den Klassenargumenten anstelle eines Attributs).In jedem Fall ist der wichtige Punkt hier, dass Ihr Klasseninitialisierer (
__call__
Methode) entweder nicht ausgeführt wird__new__
oder__init__
wenn der Schlüssel gefunden wird. Dies ist viel sauberer als die Verwendung__new__
, bei der Sie das Objekt markieren müssen, wenn Sie den Standardschritt überspringen möchten__init__
.quelle
Etwas tiefer in das hinein graben!
Der Typ einer generischen Klasse in CPython ist
type
und ihre Basisklasse istObject
(es sei denn, Sie definieren explizit eine andere Basisklasse wie eine Metaklasse). Die Reihenfolge der Anrufe auf niedriger Ebene finden Sie hier . Die erste aufgerufene Methode ist die,type_call
die danntp_new
und dann aufrufttp_init
.Der interessante Teil hier ist, dass
tp_new
dieObject
neue Methode 's (Basisklasse) aufgerufen wird, dieobject_new
atp_alloc
(PyType_GenericAlloc
) ausführt, die den Speicher für das Objekt zuweist :)Zu diesem Zeitpunkt wird das Objekt im Speicher erstellt und die
__init__
Methode aufgerufen. Wenn__init__
es nicht in deiner Klasse implementiert ist, wird dasobject_init
aufgerufen und es macht nichts :)Dann wird
type_call
nur das Objekt zurückgegeben, das an Ihre Variable gebunden ist.quelle
Man sollte es
__init__
als einfachen Konstruktor in traditionellen OO-Sprachen betrachten. Wenn Sie beispielsweise mit Java oder C ++ vertraut sind, wird dem Konstruktor implizit ein Zeiger auf seine eigene Instanz übergeben. Im Fall von Java ist es diethis
Variable. Wenn man den für Java generierten Bytecode untersuchen würde, würde man zwei Aufrufe bemerken. Der erste Aufruf bezieht sich auf eine "neue" Methode, und der nächste Aufruf bezieht sich auf die init-Methode (dies ist der tatsächliche Aufruf des benutzerdefinierten Konstruktors). Dieser zweistufige Prozess ermöglicht die Erstellung der tatsächlichen Instanz, bevor die Konstruktormethode der Klasse aufgerufen wird, die nur eine andere Methode dieser Instanz ist.Im Fall von Python gibt
__new__
es jetzt eine zusätzliche Funktion, auf die der Benutzer zugreifen kann. Java bietet diese Flexibilität aufgrund seiner Typisierung nicht. Wenn eine Sprache diese Funktion bereitstellt, kann der Implementierer von__new__
in dieser Methode viele Dinge tun, bevor er die Instanz zurückgibt, einschließlich der Erstellung einer völlig neuen Instanz eines nicht verwandten Objekts in einigen Fällen. Und dieser Ansatz eignet sich auch besonders für unveränderliche Typen im Fall von Python.quelle
Ich denke, die C ++ - Analogie wäre hier nützlich:
__new__
ordnet einfach Speicher für das Objekt zu. Die Instanzvariablen eines Objekts benötigen Speicher, um es zu halten, und dies__new__
würde der Schritt tun.__init__
Initialisieren Sie die internen Variablen des Objekts auf bestimmte Werte (kann Standard sein).quelle
Das
__init__
wird danach aufgerufen,__new__
sodass Ihr hinzugefügter Code weiterhin aufgerufen wird, wenn Sie ihn in einer Unterklasse überschreiben.Wenn Sie versuchen, eine Klasse zu unterordnen, die bereits eine hat
__new__
, kann jemand, der sich dessen nicht bewusst ist ,__init__
den Anruf zunächst anpassen und an die Unterklasse weiterleiten__init__
. Diese Konvention des Aufrufs__init__
nach__new__
hilft , dass die Arbeit wie erwartet.Das muss
__init__
noch alle Parameter berücksichtigen, die die Oberklasse__new__
benötigt, aber wenn dies nicht getan wird, wird normalerweise ein klarer Laufzeitfehler erzeugt. Und das__new__
sollte wahrscheinlich explizit*args
und '** kw' zulassen , um klar zu machen, dass die Erweiterung in Ordnung ist.Aufgrund des Verhaltens, das auf dem Originalplakat beschrieben wurde, ist es im Allgemeinen eine schlechte Form, beide
__new__
und__init__
dieselbe Klasse auf derselben Vererbungsstufe zu haben.quelle
Kein anderer Grund als der, dass es einfach so gemacht wird.
__new__
hat nicht die Verantwortung, die Klasse zu initialisieren, eine andere Methode (__call__
möglicherweise - ich weiß es nicht genau).Sie hätten
__init__
nichts tun können, wenn es bereits initialisiert worden wäre, oder Sie könnten eine neue Metaklasse mit einer neuen schreiben__call__
, die nur__init__
neue Instanzen aufruft und ansonsten nur zurückgibt__new__(...)
.quelle
Der einfache Grund ist, dass das neue zum Erstellen einer Instanz verwendet wird, während init zum Initialisieren der Instanz verwendet wird. Vor der Initialisierung sollte zuerst die Instanz erstellt werden. Deshalb sollte new vor init aufgerufen werden .
quelle
Jetzt habe ich das gleiche Problem und aus bestimmten Gründen habe ich beschlossen, Dekorateure, Fabriken und Metaklassen zu meiden. Ich habe es so gemacht:
Hauptdatei
Beispielklassen
In Benutzung
Probieren Sie es online aus!
quelle