De 'in' operator in Python bepaalt of een element zich in een collectie bevindt. Voor gebruikersobjecten, om de constructie 'x in your_obj' te ondersteunen, moet de methode __contains__ worden geïmplementeerd. Als deze er niet is, zal de interpreter proberen het object te itereren met behulp van __iter__ of __getitem__, maar het gedrag en de efficiëntie kunnen verschillen.
Voorbeeld:
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
Als je alleen __iter__ (of zelfs alleen __getitem__) implementeert, zal in werken, maar minder efficiënt en soms helemaal niet zoals verwacht.
Let op: als de collectie enorm is en de controle naïef is geïmplementeerd (bijvoorbeeld door een loop door de hele lijst), kunnen er prestatieproblemen zijn. Voor snelle controles worden bijvoorbeeld verzamelingen gebruikt.
Is het voldoende om alleen
__iter__of alleen__getitem__te implementeren voor de correcte werking van de 'in' operator? Hoe verandert het gedrag?
Antwoord:
__contains__ is, zal Python proberen de elementen te doorlopen met __iter__ (als deze er is) of __getitem__ (beginnende met index 0, totdat er een IndexError wordt gegooid).Voorbeeld:
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
Verhaal
In een project had een gebruikerscontainer voor entiteiten alleen
__iter__overschreven, daarbij vergeten__contains__te implementeren. De 'in' operator begon niet alleen traag te werken (voor grote collecties waren er vertragingen merkbaar), maar viel ook plotseling met mysterieuze fouten wanneer de iterator ten onrechte uitzonderingen gooide die geen StopIteration waren.
Verhaal
Voor een klasse, waarin elementen "on-the-fly" werden berekend op basis van index, had de ontwikkelaar alleen
__getitem__geïmplementeerd. Bij de poging om 'x in obj' met een grote x te controleren, ontstonden lange lussen en zelfs Out Of Memory — want 'in' controleert alle indices in stijgende volgorde totdat het een IndexError tegenkomt.
Verhaal
In een van de projecten was een aangepaste woordenboekomde die voor 'in' alleen op
__iter__steunde. Dit leidde ertoe dat het zoeken naar 100.000 sleutels seconden duurde in plaats van milliseconden bij de standaard dict (waarbij__contains__efficiënt is geïmplementeerd).