Ah, this is a great question!
In short, what's going on here is that the CPython internals occasionally take
shortcuts when they are doing attribute lookups, and this kind of surprising
behaviour is one of the consequences (another one being improved performance).
To understand exactly what's happening in this situation, we need to venture
into the definition of super
:
http://hg.python.org/cpython/file/c24941251473/Objects/typeobject.c#l6689
Notice specifically that it doesn't define
tp_getattr
(aka __getattr__
), but does define tp_getattro
(aka __getattribute__
):
PyTypeObject PySuper_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"super", /* tp_name */
...
0, /* tp_getattr */
...
super_getattro, /* tp_getattro */
...
};
(recall that __getattribute__
is called every time an attribute is
requested, as opposed to __getattr__
, which is only called if the attribute
doesn't exist on the object (roughly: if the attribute isn't in the object's
__dict__
)).
Next, looking into the definition of
super_getattro
(aka super.__getattribute__
), we can see the implementation is
approximately:
class super(object):
def __init__(self, obj_type, obj):
self.obj_type = obj_type
self.obj = obj
def __getattribute__(self, attr):
i = self.obj_type.__mro__.find(self.obj_type)
i += 1
while i < len(obj_type.__mro__):
cur_type = self.obj_type.__mro__[i]
cur_dict = cur_type.__dict___
res = cur_dict.get(attr)
if res is not None:
return res
i += 1
return object.__getattribute__(self, attr)
Which makes it obvious why super
doesn't play well with __getattr__
—
super
is only checking for attributes in the parent class' __dict__
!
Fun aside: it seems like pypy
(as of 2.1.0) behaves the same way:
$ pypy super.py
Traceback (most recent call last):
File "app_main.py", line 72, in run_toplevel
File "super.py", line 16, in <module>
print o.foo()
File "super.py", line 13, in foo
return super(SubClass2, self).foo() + suffix
File "super.py", line 8, in foo
return super(SubClass, self).foo() + suffix
AttributeError: 'super' object has no attribute 'foo'