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: special method lookup docs don't address some important details
Type: behavior Stage: resolved
Components: Documentation Versions: Python 3.1, Python 3.2, Python 3.3, Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: Trundle, docs@python, eric.snow, georg.brandl, r.david.murray, rhettinger
Priority: normal Keywords:

Created on 2011-05-03 20:47 by r.david.murray, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (5)
msg135068 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2011-05-03 20:47
The following code:

--------------------------------
class X(list):

    def __contains__(self, key):
        print('X contains:', key)


class Y():

    def __init__(self, x):
        self.x = x

    def __getattr__(self, key):
        return getattr(self.x, key)

    def __iter__(self):
        print('Y iter')
        return iter([1,2])

x = X()
y = Y(x)

print('res:', 1 in y)
-----------------------------

prints True.  It has been explained to me that this is because of:

http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes

However, there is no way in the world that I would guess the behavior above from the documentation provided (and I find it surprising...I expected x's __contains__ to get called because Y (a class, not an instance) doesn't have a __contains__ method).

Can anyone explain it more clearly and update the documentation?
msg135074 - (view) Author: Eric Snow (eric.snow) * (Python committer) Date: 2011-05-03 21:18
In 2.7 I get the following (if Y does not inherit from object):

>>> print('res:', 1 in y)
('X contains:', 1)
('res:', False)

This makes sense with old style classes.  With Y(object):

>>> print('res:', 1 in y)
Y iter
('res:', True)
msg135075 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2011-05-03 21:19
Not sure I understand your issue here.  How should "1 in y" get at X.__contains__ given the special method lookup rules?  The __getattr__ is not called since y.__contains__ isn't looked up.
msg135080 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2011-05-03 21:43
Well, then I suppose my question is why isn't __contains__ looked up?  Other special methods that don't exist on Y do cause __getattr__ to be called.  Why is __contains__ special?  The docs for __getattr__ don't hint at this possibility either.

I think the people that understand this must have a mental model of how the interpreter looks up methods that has some pieces that are missing from mine.  I guess I'd like those bits to be made explicit in the special method lookup docs, or if they are already documented somewhere, for there to be a pointer to them in the special method lookup docs.
msg135082 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2011-05-03 22:01
Ah, that's what my problem is.  My test example was poorly conceived (I used __del__!) so I *thought* the other special methods were triggering getattr.

I'd have figured it out if I hadn't screwed up my test :(
History
Date User Action Args
2022-04-11 14:57:16adminsetgithub: 56197
2011-05-03 22:01:55r.david.murraysetstatus: open -> closed
resolution: not a bug
messages: + msg135082

stage: needs patch -> resolved
2011-05-03 21:43:39r.david.murraysetmessages: + msg135080
2011-05-03 21:19:18georg.brandlsetnosy: + georg.brandl
messages: + msg135075
2011-05-03 21:18:11eric.snowsetnosy: + eric.snow
messages: + msg135074
2011-05-03 20:56:10rhettingersetassignee: docs@python -> rhettinger

nosy: + rhettinger
2011-05-03 20:49:04Trundlesetnosy: + Trundle
2011-05-03 20:47:09r.david.murraycreate