The in operator checks whether an object is a member of a container (like a list, set, dict, or any custom class). Under the hood, it translates directly to a call to the container’s __contains__ method.
Here’s exactly how __contains__ affects that behaviour:
1. When __contains__ is defined
If a class defines the special method __contains__(self, item), the expression
item in containeris equivalent to
container.__contains__(item)Python uses the truthiness of whatever __contains__ returns to decide the result:
Return
True→inevaluates toTrueReturn
False→inevaluates toFalseReturn any other value → converted to
bool(truthy =True, falsy =False)
class AlwaysYes: def __contains__(self, item): return 1 # truthy → always True class AlwaysNo: def __contains__(self, item): return 0 # falsy → always False print(42 in AlwaysYes()) # True print(42 in AlwaysNo()) # False
2. When __contains__ is not defined
If the class does not have __contains__, Python falls back in this order:
Use
__iter__– iterate over the object and compare each yielded element toitemusing==.Use
__getitem__– if no__iter__, but__getitem__is defined, Python accesses indices0, 1, 2, ...and compares each toitemuntil anIndexErroris raised (old‑style sequence protocol).If neither is available, a
TypeErroris raised.
This fallback is why you can write x in [1, 2, 3] or x in range(10) even though list and range don’t expose __contains__ directly – they rely on iteration.
3. Custom membership logic
By overriding __contains__ you can completely redefine what “membership” means for your objects. For example, a fuzzy string matcher:
class FuzzyStr: def __init__(self, text): self.text = text def __contains__(self, sub): return sub.lower() in self.text.lower() s = FuzzyStr("Hello World") print("hello" in s) # True – case‑insensitive match
4. Important notes
__contains__is only called on the right‑hand object (container). The left‑handitemcan be anything; no special method is required on it.The
not inoperator simply negates the result of__contains__(or its fallback).For dictionaries,
key in dcallsdict.__contains__, which looks for the key, not the value.If
__contains__explicitly returns a non‑boolean, the standard truth‑testing rules apply (e.g.,0,None,"", empty containers are falsy; everything else is truthy). This can lead to surprising results if you accidentally return a value likeNone, so it’s best to always explicitly return abool.
In short: __contains__ replaces the default iterative search with whatever custom logic you define, giving you full control over the in operator.
Note: If __contains__ returns None, the in operator will evaluate to False. This happens because in uses the truthiness of the return value, and None is falsy.
All Questions From This Chapter « Previously Next »

No comments:
Post a Comment