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.
Reicht es aus, nur
__iter__oder nur__getitem__zu implementieren, um den Operatorinkorrekt zu verwenden? Wie würde sich das Verhalten ändern?
Antwort:
__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.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
Geschichte
In einem Projekt hat ein benutzerdefinierter Container für die Speicherung von Entitäten nur
__iter__überschrieben und__contains__vergessen. Der Operatorinarbeitete 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 objmit großem x zu überprüfen, traten lange Schleifen und sogar Out Of Memory auf - denninü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
innur 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.