Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python __getitem__ and in operator result in strange behavior

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 
like image 632
Matthew Moisen Avatar asked Dec 11 '19 22:12

Matthew Moisen


1 Answers

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.)

like image 163
user2357112 supports Monica Avatar answered Oct 06 '22 05:10

user2357112 supports Monica