In Python ist die Erstellung eines Klassenobjekts ein zweistufiger Prozess:
__new__ aufgerufen, die für die Zuweisung und Rückgabe einer neuen Instanz der Klasse verantwortlich ist.__init__ aufgerufen, der das bereits erstellte Objekt initialisiert.__new__ — Erstellung (kann jedes neue Objekt zurückgeben; erforderlich ist der Parameter — Klasse).__init__ — Initialisierung (arbeitet mit self, bereits zugewiesenem Exemplar).__new__ für Erben von unveränderlichen Typen (tuple, str, int), zur Erstellung von Singletons und dem "Fabrik"-Muster überschrieben.class MyStr(str): def __new__(cls, value): print("__new__ aufgerufen") instance = super().__new__(cls, value.upper()) # Wert vor der Erstellung ändern return instance def __init__(self, value): print("__init__ aufgerufen", value) s = MyStr('abc') # __new__ aufgerufen -> __init__ aufgerufen abc print(s) # 'ABC'
Kann man ein unveränderliches Objekt über __init__ initialisieren, wenn im Klassen keine __new__-Methode implementiert ist?
Nein! Zum Beispiel müssen für unveränderliche Typen (wie bei str, int, tuple) alle Änderungen an Feldern/Werten in __new__ vorgenommen werden, andernfalls können Werte in __init__ nicht geändert werden.
class MyTuple(tuple): def __init__(self, items): print(f'__init__! {items}') def __new__(cls, items): print(f'__new__! {items}') return super().__new__(cls, map(str, items)) t = MyTuple([1, 2, 3]) print(t) # ('1', '2', '3')
Geschichte
In einem großen Projekt in Django wurde das Erbe von str implementiert, um spezielle Typen von Zeichenfolgen zu speichern. Sie versuchten, den Wert in __init__ zu ändern – aber das Ergebnis änderte sich nicht, da str unveränderlich ist. Sie haben das nur nach dem Studium und der Überschreibung von __new__ korrigiert.
Geschichte
Bei der Implementierung des Singleton-Musters wurde vergessen, die Logik für die Rückgabe einer bestehenden Instanz in __new__ zu implementieren: Mehrere Aufrufe der Klasse führten weiterhin zur Erstellung neuer Objekte, was zu Speicherfragmentierung führte.
Geschichte
In einer Bibliothek zur Datenserialisierung wurde beim Caching eine Klasse mit überschriebenem __init__ verwendet, aber es wurde nicht bemerkt, dass für das Caching tatsächlich die Rückgabe desselben Objekts über __new__ erforderlich ist. Dadurch brach der Cache bei jedem neuen Aufruf zusammen, da unterschiedliche Objekte erstellt wurden.