[issue11988] special method lookup docs don't address some important details

New submission from R. David Murray <rdmurray@bitdance.com>: 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-ne... 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? ---------- assignee: docs@python components: Documentation messages: 135068 nosy: docs@python, r.david.murray priority: normal severity: normal stage: needs patch status: open title: special method lookup docs don't address some important details type: behavior versions: Python 2.7, Python 3.1, Python 3.2, Python 3.3 _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

Changes by Andreas Stührk <andy-python@hammerhartes.de>: ---------- nosy: +Trundle _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

Changes by Raymond Hettinger <raymond.hettinger@gmail.com>: ---------- assignee: docs@python -> rhettinger nosy: +rhettinger _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

Eric Snow <ericsnowcurrently@gmail.com> added the comment: In 2.7 I get the following (if Y does not inherit from object):
This makes sense with old style classes. With Y(object):
---------- nosy: +ericsnow _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

Georg Brandl <georg@python.org> added the comment: 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. ---------- nosy: +georg.brandl _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

R. David Murray <rdmurray@bitdance.com> added the comment: 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. ---------- _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

R. David Murray <rdmurray@bitdance.com> added the comment: 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 :( ---------- resolution: -> invalid stage: needs patch -> committed/rejected status: open -> closed _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

Changes by Andreas Stührk <andy-python@hammerhartes.de>: ---------- nosy: +Trundle _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

Changes by Raymond Hettinger <raymond.hettinger@gmail.com>: ---------- assignee: docs@python -> rhettinger nosy: +rhettinger _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

Eric Snow <ericsnowcurrently@gmail.com> added the comment: In 2.7 I get the following (if Y does not inherit from object):
This makes sense with old style classes. With Y(object):
---------- nosy: +ericsnow _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

Georg Brandl <georg@python.org> added the comment: 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. ---------- nosy: +georg.brandl _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

R. David Murray <rdmurray@bitdance.com> added the comment: 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. ---------- _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________

R. David Murray <rdmurray@bitdance.com> added the comment: 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 :( ---------- resolution: -> invalid stage: needs patch -> committed/rejected status: open -> closed _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue11988> _______________________________________
participants (5)
-
Andreas Stührk
-
Eric Snow
-
Georg Brandl
-
R. David Murray
-
Raymond Hettinger