ProgrammierungMiddle Python Entwickler

Wie funktioniert der Operator 'in' für benutzerdefinierte Objekte in Python? Was muss in der Klasse implementiert werden, damit der Ausdruck 'x in your_obj' funktioniert? Wie kann man Leistungsprobleme und unerwartete Fehler vermeiden?

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

Antwort.

Der Operator in in Python bestimmt, ob ein Element in einer Sammlung enthalten ist. Für benutzerdefinierte Objekte muss die Methode __contains__ implementiert werden, damit die Konstruktion x in your_obj unterstützt wird. Wenn diese nicht vorhanden ist, versucht der Interpreter, das Objekt mit __iter__ oder __getitem__ zu iterieren, aber das Verhalten und die Effizienz können unterschiedlich sein.

Beispiel:

class MyBag: def __init__(self, items): self.items = items def __contains__(self, value): return value in self.items bag = MyBag([1,2,3]) print(2 in bag) # True print(5 in bag) # False

Wenn nur __iter__ (oder sogar nur __getitem__) implementiert wird, funktioniert in, ist aber weniger effizient und verhält sich manchmal ganz anders als erwartet.

Hinweis: Wenn die Sammlung riesig ist und die Überprüfung naiv implementiert ist (z. B. durch eine Schleife über die gesamte Liste), können Leistungsprobleme auftreten. Für schnelle Überprüfungen verwenden wir beispielsweise Mengen.

Trickfrage.

Reicht es aus, nur __iter__ oder nur __getitem__ zu implementieren, um den Operator in korrekt zu verwenden? Wie würde sich das Verhalten ändern?

Antwort:

  • Wenn __contains__ fehlt, wird Python versuchen, die Elemente über __iter__ (falls vorhanden) oder __getitem__ (beginnend bei Index 0, bis eine IndexError-Ausnahme ausgeworfen wird) zu iterieren.
  • Dieses Verhalten ist weniger effizient und kann zu unendlichen Schleifen oder Ausnahmen führen, wenn die Methoden mit Tippfehlern implementiert sind.

Beispiel:

class Weird: def __getitem__(self, idx): if idx < 3: return idx raise IndexError w = Weird() print(2 in w) # True print(5 in w) # False

Beispiele für reale Fehler aufgrund mangelnden Wissens über die Feinheiten des Themas.


Geschichte

In einem Projekt hat ein benutzerdefinierter Container für die Speicherung von Entitäten nur __iter__ überschrieben und __contains__ vergessen. Der Operator in arbeitete nicht nur langsam (bei großen Sammlungen traten merkliche Verzögerungen auf), sondern fiel auch plötzlich mit mysteriösen Fehlern aus, wenn der Iterator fälschlicherweise Ausnahmen anderer Art als StopIteration auslöste.


Geschichte

Für eine Klasse, in der Elemente „on the fly“ nach Index berechnet wurden, implementierte der Entwickler nur __getitem__. Bei dem Versuch, x in obj mit großem x zu überprüfen, traten lange Schleifen und sogar Out Of Memory auf - denn in überprüft alle Indizes in aufsteigender Reihenfolge, bis es auf IndexError stößt.


Geschichte

In einem der Projekte wurde ein benutzerdefiniertes Wörterbuch implementiert, welches sich für in nur auf __iter__ verließ. Das führte dazu, dass die Suche nach 100.000 Schlüsseln Sekunden dauerte, im Gegensatz zu Millisekunden bei dict, wo __contains__ effizient implementiert ist.