Consider the following two classes:
class Foo0(object):
pass
class Foo1(object):
def __contains__(self, _):
return False
Neither of them is iterable, as can be observed by trying for i in Foo1(): pass
:
Traceback (most recent call last):
File "stuff.py", line 11, in <module>
for i in Foo1(): pass
TypeError: 'Foo1' object is not iterable
Conversely, 3 in Foo1()
is valid (as Foo1
has __contains__
), whereas 3 in Foo0()
is not:
Traceback (most recent call last):
File "stuff.py", line 9, in <module>
3 in Foo0()
TypeError: argument of type 'Foo0' is not iterable
I find the error misleading. The point is not that Foo0
is not iterable - after all Foo1
is not iterable too, so that's not the inherent reason. Moreover, as @niemmi and @tobias_k correctly note below, if a class does not support __contains__
, __iter__
is used as a fallback, which is the direct cause for this error. However this makes things worse, as a user might try to solve this error by implementing __iter__
, which is a very inefficient means of implementing __contains__
.
Why was this chosen as the error, then?
If the object doesn't have __contains__
method Python automatically tries to iterate over it to see if it can find the element. Since Foo0
isn't an iterable you will see the error.
UPDATE: Python language reference explains this in bit more detail:
For user-defined classes which define the contains() method, x in y is true if and only if y.contains(x) is true.
For user-defined classes which do not define contains() but do define iter(), x in y is true if some value z with x == z is produced while iterating over y. If an exception is raised during the iteration, it is as if in raised that exception.
Lastly, the old-style iteration protocol is tried: if a class defines getitem(), x in y is true if and only if there is a non-negative integer index i such that x == y[i], and all lower integer indices do not raise IndexError exception. (If any other exception is raised, it is as if in raised that exception).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With