This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: "in" expression falls back to __iter__ before __getitem__
Type: Stage:
Components: Documentation Versions: Python 3.1, Python 3.2, Python 2.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: georg.brandl Nosy List: afoglia, georg.brandl, rhettinger, terry.reedy
Priority: normal Keywords:

Created on 2009-06-22 19:27 by afoglia, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Messages (6)
msg89607 - (view) Author: Anthony Foglia (afoglia) Date: 2009-06-22 19:27
I was debugging a class where I defined __getitem__ and __iter__, but 
not __contains__.  The documentation describing this case (at the end of 
section 5.9) is old and hasn't been updated for the iterator protocol.

It should read something like:

"For user-defined classes which do not define __contains__() and do 
define __iter__() or __getitem__(), x in y is true if and only if there 
is a value z reachable from iter(y) before iter(y) throws a 
StopIteration exception. (If any other exception is raised, it is as if 
in raised that exception)."

Or something better worded.

(I'm using Python 2.5, but I really doubt things have changes in 2.6 or 
2.7.  I don't know enough about 3.0 to know either way.)
msg89743 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2009-06-26 21:25
In 3.1, Section 5.9 has
"
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__() and do
define __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).
"
However, discussion of how user-defined classes emulate builtins is
mostly in 3.3. Special method names. I think some of the above should be
moved, with or without revision, (or copied) to 3.3.5. Emulating
container types.

The current entry there says only 
"
object.__contains__(self, item) 
Called to implement membership test operators. Should return true if
item is in self, false otherwise. For mapping objects, this should
consider the keys of the mapping rather than the values or the key-item
pairs.
"
which seems to me inadequate, as it does not discuss the alternative, as
many other entries in 3.3 do.

Regardless of that, I verified that in 3.1, __iter__ is called in
preference to __getitem__:

class C():
    def __iter__(s):
        print('in iter')
        return iter([])
    def __getitem(s,i):
        print('in getitem')

print(1 in C())

prints
in iter
False

so some change is needed.
msg89744 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2009-06-26 21:37
Side note: 2.5, including, I believe, the docs, is frozen except for
security fixes. If you find 2.5 doc issues, please check the current 2.x
development version, currently http://docs.python.org/dev/ to see if
they have been fixed or not.
msg89745 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-06-26 21:49
3.0 is no longer supported either
msg94335 - (view) Author: Anthony Foglia (afoglia) Date: 2009-10-22 06:20
I've added Python 2.7 to the list of versions.  The development docs
have the same issue.

Let me try another stab at what the docs should say.  Following the
suggestion to add it to 3.3.5, perhaps it should be:

"
object.__contains__(self, item)
    Called to implement membership test operators. Should return true if
item is in self, false otherwise. For mapping objects, this should
consider the keys of the mapping rather than the values or the key-item
pairs.

    If __contains__ is not provided but __iter__ is provided, the
membership test "x in y" is true if and only if there is a value z
reachable by iterating iter(y) before iter(y) throws a StopIteration
exception.  (If any other exception is raised, it is as if
in raised that exception).

    If neither __contains__ nor __iter__ are provided but __getitem__ is
provided, the membership test "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).
"

I worry that this is becoming more a description of "in" rather than
__contains__. 

Either way, the last sentence in paragraph 6 of section 5.9 should refer
not just to 3.3.1 (Basic Customization) but also 3.3.5 (Emulating
Container Types).  The latter is where __contains__, __iter__ and
__getitem__ are defined.  Perhaps the new wording should be, "You can
control comparison behavior of objects of non-built-in types by defining
rich comparison methods like __gt__(), described in section Basic
customization, and the membership test method __contains__(), described
in section Emulating container types."
msg94347 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2009-10-22 15:00
Thanks, fixed in r75606.
History
Date User Action Args
2022-04-11 14:56:50adminsetgithub: 50573
2009-10-22 15:00:19georg.brandlsetstatus: open -> closed
resolution: fixed
messages: + msg94347
2009-10-22 06:20:10afogliasetmessages: + msg94335
versions: + Python 2.7
2009-10-21 20:43:06rhettingersetassignee: rhettinger -> georg.brandl
2009-06-26 21:49:13rhettingersetmessages: + msg89745
versions: - Python 3.0
2009-06-26 21:37:17terry.reedysetmessages: + msg89744
versions: + Python 3.0, Python 3.1, Python 3.2, - Python 2.6, Python 2.5, Python 2.7
2009-06-26 21:25:57terry.reedysetnosy: + terry.reedy
messages: + msg89743
2009-06-22 20:16:42rhettingersetassignee: georg.brandl -> rhettinger

nosy: + rhettinger
2009-06-22 19:27:52afogliacreate