简体   繁体   中英

What is the difference between super(cls, instance) and super(cls, subclass)?

super(cls, instance)super(cls, subclass)不会都返回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, eg 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 (ie 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 .

super(cls, instance).attr inspects the MRO of the class of instance (ie instance.__class__.__mro__ ), looks up the next class after cls in the MRO that has an attribute attr , and returns the result of attr.__get__(instance, instance.__class__) if it has a __get__ method or returns attr if it has no __get__ method. This case is used in functions :

>>> class A:
...     def f(self): return 'A.f'
... 
>>> class B(A):
...     def f(self): return 'B.f ' + super(B, self).f()
... 
>>> B().f()
'B.f A.f'

super(cls, subclass).attr inspects the MRO of subclass (ie subclass.__mro__ ), looks up the next class after cls in the MRO that has an attribute attr , and returns the result of attr.__get__(None, subclass) if it has a __get__ method or returns attr if it has no __get__ method. This case is used in classmethod :

>>> class A:
...     @classmethod
...     def f(cls): return 'A.f'
... 
>>> class B(A):
...     @classmethod
...     def f(cls): return 'B.f ' + super(B, cls).f()
... 
>>> B.f()
'B.f A.f'

For function attributes, super(cls, instance).attr returns a bound method of instance , while super(cls, subclass).attr returns a function :

>>> class A:
...     def f(self): return 'A.f'
... 
>>> class B(A):
...     def f(self): return 'B.f'
... 
>>> b = B()
>>> b.f
<bound method B.f of <__main__.B object at 0x10e7d3fa0>>
>>> B.f
<function B.f at 0x10e7ea790>
>>> b.f()
'B.f'
>>> B.f(b)
'B.f'
>>> super(B, b).f
<bound method A.f of <__main__.B object at 0x10e7d3fa0>>
>>> super(B, B).f
<function A.f at 0x10e7ea700>
>>> super(B, b).f()
'A.f'
>>> super(B, B).f(b)
'A.f'

For classmethod attributes, super(cls, instance).attr and super(cls, subclass).attr return a bound method of respectively instance.__class__ and subclass :

>>> class A:
...     @classmethod
...     def f(cls): return 'A.f'
... 
>>> class B(A):
...     @classmethod
...     def f(cls): return 'B.f'
... 
>>> b = B()
>>> b.f
<bound method B.f of <class '__main__.B'>>
>>> B.f
<bound method B.f of <class '__main__.B'>>
>>> b.f()
'B.f'
>>> B.f()
'B.f'
>>> super(B, b).f
<bound method A.f of <class '__main__.B'>>
>>> super(B, B).f
<bound method A.f of <class '__main__.B'>>
>>> super(B, b).f()
'A.f'
>>> super(B, B).f()
'A.f'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM