super - python: cooperative supercall of __getattr__ -
i'm working somethign similar code:
class baseclass(object): def __getattr__(self, attr): return lambda:'1' class subclass(baseclass): def foo(self): suffix = '2' return super(subclass, self).foo() + suffix class subclass2(subclass): def foo(self): suffix = '3' return super(subclass2, self).foo() + suffix o = subclass2() print o.foo()
i'd expect see output of '123', instead error attributeerror: 'super' object has no attribute 'foo'
. python isn't attempting use base class's __getattr__
.
without modifying base class, , keeping 2 super calls similar, i'm not able output want. there cooperative supercall pattern work me here?
i understand super() overrides getattr in way needs do, i'm asking if there's reasonable workaround allows subclass's __getattr__
called when appropriate.
ah, great question!
in short, what's going on here cpython internals take shortcuts when doing attribute lookups, , kind of surprising behaviour 1 of consequences (another 1 being improved performance).
to understand what's happening in situation, need venture definition of super
: http://hg.python.org/cpython/file/c24941251473/objects/typeobject.c#l6689
notice doesn't define tp_getattr
(aka __getattr__
), 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 __getattribute__
called every time attribute requested, opposed __getattr__
, called if attribute doesn't exist on object (roughly: if attribute isn't in object's __dict__
)).
next, looking definition of super_getattro
(aka super.__getattribute__
), can see implementation approximately:
class super(object): def __init__(self, obj_type, obj): self.obj_type = obj_type self.obj = obj def __getattribute__(self, attr): = self.obj_type.__mro__.find(self.obj_type) += 1 while < len(obj_type.__mro__): cur_type = self.obj_type.__mro__[i] cur_dict = cur_type.__dict___ res = cur_dict.get(attr) if res not none: return res += 1 return object.__getattribute__(self, attr)
which makes obvious why super
doesn't play __getattr__
— super
checking attributes in parent class' __dict__
!
fun aside: seems pypy
(as of 2.1.0) behaves 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'
Comments
Post a Comment