Strange Behavior with Old-Style classes and implicit __contains__

rconradharris at gmail.com rconradharris at gmail.com
Thu Apr 12 01:37:35 CEST 2007


A co-worker of mine came across some interesting behavior in the
Python interpreter today and I'm hoping someone more knowledgeable in
Python internals can explain this to me.

First, we create an instance of an Old-Style class without defining a
__contains__ but instead define a __getitem__ method in which we raise
KeyError. Next we repeatedly use the 'in' operator to test to see
whether something, a string, an int, etc is an attribute of this new
instance.

Here's the strange part: The first test will return False as if the
__getitem__ was never called. The next test will raise a KeyError as
we'd expect. The test after that will again return False. This goes on
ad infinitum.

 In Code:
  Python 2.5 (r25:51908, Jan 21 2007, 03:10:25)
  [GCC 3.4.6 20060404 (Red Hat 3.4.6-3)] on linux2
  Type "help", "copyright", "credits" or "license" for more
information.
  >>> class Foo:
  ...     def __getitem__(self, key):
  ...             raise KeyError
  ...
  >>> foo = Foo()
  >>> "asdf" in foo
  False
  >>> "asdf" in foo
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 3, in __getitem__
  KeyError
  >>> "asdf" in foo
  False
  >>> "asdf" in foo
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 3, in __getitem__
  KeyError


According to the language reference, if __contains__ isn't defined for
Old-Style classes and __getitem__ is defined, __getitem__ will be
called. So, how then can False ever be returned?

And to make matters worse, I've set up a situation where Python will
flat-out return incorrect results:

Python 2.5 (r25:51908, Jan 21 2007, 03:10:25)
[GCC 3.4.6 20060404 (Red Hat 3.4.6-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
...     def __getitem__(self, key):
...             raise KeyError
...
>>> foo = Foo()
>>> "asdf" in foo
False
>>> 1 in set([1,2,3])      <---- So the prior KeyError from another class is interacting and producing bad output
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __getitem__
KeyError

According to our cursory testing, this funny business doesn't happen
with New-Style Classes or when using PyPy.

If anyone can provide some insight into this, it would be greatly
appreciated.

Thanks,
Rick Harris




More information about the Python-list mailing list