ProgrammierungSenior Python Entwickler

Wie funktioniert die Methode __new__ in Python und wodurch unterscheidet sie sich von __init__? Wann und warum sollte man __new__ anstelle von __init__ überschreiben? Geben Sie ein detailliertes Beispiel für die Verwendung.

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

In Python ist die Erstellung eines Klassenobjekts ein zweistufiger Prozess:

  1. Zuerst wird die statische Methode __new__ aufgerufen, die für die Zuweisung und Rückgabe einer neuen Instanz der Klasse verantwortlich ist.
  2. Dann wird __init__ aufgerufen, der das bereits erstellte Objekt initialisiert.

Unterschiede:

  • __new__ — Erstellung (kann jedes neue Objekt zurückgeben; erforderlich ist der Parameter — Klasse).
  • __init__ — Initialisierung (arbeitet mit self, bereits zugewiesenem Exemplar).
  • Normalerweise wird __new__ für Erben von unveränderlichen Typen (tuple, str, int), zur Erstellung von Singletons und dem "Fabrik"-Muster überschrieben.

Beispiel für die Verwendung:

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'

Trickfrage.

Kann man ein unveränderliches Objekt über __init__ initialisieren, wenn im Klassen keine __new__-Methode implementiert ist?

Antwort mit Beispiel:

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')

Beispiele für echte Fehler aufgrund mangelnder Kenntnisse des Themas


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.