Reputation: 18289
What explains the following behavior:
class Foo:
def __getitem__(self, item):
print("?")
return 1
f = Foo()
1 in f # prints one ? and returns True
5 in f # prints ? forever until you raise a Keyboard Exception
# Edit: eventually this fails with OverflowError: iter index too large
Upvotes: 36
Views: 1612
Reputation: 281046
If an object doesn't have a __contains__
implementation, in
falls back on a default that basically works like this:
def default__contains__(self, element):
for thing in self:
if thing == element:
return True
return False
And if an object doesn't have an __iter__
implementation, for
falls back on a default that basically works like this:
def default__iter__(self):
i = 0
try:
while True:
yield self[i]
i += 1
except IndexError:
pass
These defaults are used even if the object is not intended to be a sequence.
Your 1 in f
and 5 in f
tests are using the default fallbacks for in
and for
, leading to the observed behavior. 1 in f
finds 1
immediately, but your __getitem__
never returns 5
, so 5 in f
runs forever.
(Well, actually, on the reference implementation of Python, the default __iter__
fallback stores the index in a C-level variable of type Py_ssize_t
, so if you wait long enough, that variable maxes out and Python raises an OverflowError. If you saw that, you must be on a 32-bit Python build. Computers haven't existed long enough for anyone to hit that on a 64-bit Python.)
Upvotes: 49