Won’t super(cls, instance)
and super(cls, subclass)
both return the superclass of cls
?
The difference is huge; super()
with a type (class) second argument instead of an object (instance) gives you unbound methods, not bound methods (just like accessing those methods on a class would).
I'll explain first how super()
works with an instance second argument.
super()
inspects the MRO of self
, finds the first argument (type
or supertype
) in the MRO, then finds the next object that has the requested attribute.
Demo:
>>> class BaseClass(object):
... def foo(self): return 'BaseClass foo'
...
>>> class Intermediary(BaseClass):
... def foo(self): return 'Intermediary foo'
...
>>> class Derived(Intermediary):
... def foo(self): return 'Derived foo'
...
>>> d = Derived()
>>> d.foo()
'Derived foo'
>>> super(Derived, d).foo
<bound method Intermediary.foo of <__main__.Derived object at 0x10ef4de90>>
>>> super(Derived, d).foo()
'Intermediary foo'
>>> super(Intermediary, d).foo()
'BaseClass foo'
>>> Derived.__mro__
(<class '__main__.Derived'>, <class '__main__.Intermediary'>, <class '__main__.BaseClass'>, <type 'object'>)
The MRO of Derived
is (Derived, Intermediary, BaseClass)
; super()
finds this MRO by looking at the second argument, using type(d).__mro__
. The search for foo
starts at the next class after the first argument given.
The foo()
method is bound here, you can just call it.
If you give super()
a type as the second argument, then it'll use the MRO of that type, e.g. instead of using type(instance).__mro__
it just goes for type.__mro__
. However it then has no instance to bind the methods to. super(supertype, type).foo
is just the (unbound) function object:
>>> super(Intermediary, Derived).foo
<function BaseClass.foo at 0x106dd6040>
>>> super(Intermediary, Derived).foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() missing 1 required positional argument: 'self'
>>> super(Intermediary, Derived).foo(d)
'BaseClass foo'
To call .foo()
I have to explicitly pass in a self
argument.
(In Python 2, the above would return a foo
unbound method object instead of a function, but the principle is the same).
The method returned is also, again, from the next class in the MRO chain; BaseClass.foo
was returned there.
This is down to the function.__get__
method (i.e. the descriptor protocol, responsible for binding), as it returns itself (or, in Python 2, an unbound method) when passed a class to bind to. (For classmethod
objects, __get__
does return a bound object when passed in a class).
So, TL;DR, for methods super(type, object)
returns a bound method, super(supertype, type)
returns unbound methods. The goal was to make it possible to store this object as a class-private attribute to avoid having to keep looking up the class object, see How to use super() with one argument?. It's use-case has been obsoleted entirely in Python 3 so it is slated for deprecation.