I try to access the classmethod of a parent from within __init_subclass__
however that doesn't seem to work.
Suppose the following example code:
class Foo:
def __init_subclass__(cls):
print('init', cls, cls.__mro__)
super(cls).foo()
@classmethod
def foo(cls):
print('foo')
class Bar(Foo):
pass
which produces the following exception:
AttributeError: 'super' object has no attribute 'foo'
The cls.__mro__
however shows that Foo
is a part of it: (<class '__main__.Bar'>, <class '__main__.Foo'>, <class 'object'>)
.
So I don't understand why super(cls).foo()
doesn't dispatch to Foo.foo
. Can someone explain this?
A normal super
object (what you normally get from calling super(MyType, self)
or super()
or super(MyType, myobj)
) keeps track of both the type and the object it was created with. Whenever you look up an attribute on the super
, it skips over MyType
in the method resolution order, but if it finds a method it binds it to that self
object.
An unbound super
has no self
object. So, super(cls)
skips over cls
in the MRO to find the method foo
, and then binds it to… oops, it has nothing to call it on.
So, what things can you call a classmethod
on? The class itself, or a subclass of it, or an instance of that class or subclass. So, any of those will work as the second argument to super
here, the most obvious one being:
super(cls, cls)
This is somewhat similar to the difference between staticmethods (bound staticmethods
are actually bound to nothing) and classmethods
(bound classmethods
are bound to the class instead of an instance), but it's not quite that simple.
If you want to know why an unbound super
doesn't work, you have to understand what an unbound super
really is. Unfortunately, the only explanation in the docs is:
If the second argument is omitted, the super object returned is unbound.
What does this mean? Well, you can try to work it out from first principles as a parallel to what it means for a method to be unbound (except, of course, that unbound methods aren't a thing in modern Python), or you can read the C source, or the original introduction to 2.2's class-type unification (including a pure-Python super
clone).
A super
object has a __self__
attribute, just like a method object. And super(cls)
is missing its __self__
, just like str.split
is.1
You can't use an unbound super
explicitly the way you can with an unbound method (e.g., str.split('123', '2')
does the same as '123'.split('2')
, but super(cls).foo(cls)
doesn't work the same as super(cls, cls).foo()
). But you can use them implicitly, the same way you do with unbound methods all the time without normally thinking about it.
If you don't know how methods work, the tl'dr is: when you evaluate myobj.mymeth
, Python looks up mymeth
, doesn't find it on myobj
itself, but does find it on the type, so it checks whether it's a non-data descriptor, and, if so, calls its __get__
method to bind it to myobj
.
So, unbound methods2 are non-data descriptors whose __get__
method returns a bound method. Unbound @classmethod
s are similar, but their __get__
ignores the object and returns a bound method bound to the class. And so on.
And unbound super
s are non-data descriptors whose __get__
method returns a bound super
.
Example (credit to wim for coming up with the closest thing to a use for unbound super
that I've seen):
class A:
def f(self): print('A.f')
class B(A):
def f(self): print('B.f')
b = B()
bs = super(B)
B.bs = bs
b.bs.f()
We created an unbound super bs
, stuck it on the type B
, and then b.bs
is a normal bound super, so b.bs.f
is A.f
, just like super().f
would have been inside a B
method.
Why would you want to do that? I'm not sure. I've written all kinds of ridiculously dynamic and reflective code in Python (e.g., for transparent proxies to other interpreters), and I can't remember ever needing an unbound super
. But if you ever need it, it's there.
1. I'm cheating a bit here. First, unbound methods aren't a thing anymore in Python 3—but functions work the same way, so Python uses them where it used to use unbound methods. Second, str.split
, being a C builtin, wasn't properly an unbound method even in 2.x—but it acts like one anyway, at least as far as we're concerned here.
2. Actually plain-old functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With