在阅读 Python 文档时super()
,我偶然发现了以下语句:
如果省略第二个参数,则返回的超级对象是未绑定的。
“未绑定”是什么意思以及如何super()
与一个参数一起使用?
Python 函数对象是描述符,Python 使用描述符协议将函数绑定到实例。这个过程产生一个绑定方法。
绑定是self
在您调用方法时使“魔术”参数出现的原因,也是property
在您尝试将属性用作实例的属性时使对象自动调用方法的原因。
super()
当您尝试使用它来查找父类上的方法时,使用两个参数调用相同的描述符协议;super(Foo, self).bar()
将遍历Foo
父类,直到bar
找到一个属性,如果这是一个描述符对象,它将被绑定到self
. 调用bar
然后调用绑定方法,该方法又调用作为参数传入的self
函数bar(self)
。
为此,super()
对象存储self
要绑定的类(第一个参数)和(第二个参数),以及参数的类型self
作为属性__thisclass__
,__self__
并且__self_class__
分别:
>>> class Foo:
... def bar(self):
... return 'bar on Foo'
...
>>> class Spam(Foo):
... def bar(self):
... return 'bar on Spam'
...
>>> spam = Spam()
>>> super(Spam, spam)
<super: <class 'Spam'>, <Spam object>>
>>> super(Spam, spam).__thisclass__
<class '__main__.Spam'>
>>> super(Spam, spam).__self__
<__main__.Spam object at 0x107195c10>
>>> super(Spam, spam).__self_class__
<class '__main__.Spam'>
查找属性时,搜索该__mro__
属性的__self_class__
属性,从 的位置后一个位置开始__thisclass__
,并绑定结果。
super()
只有一个参数将其__self__
和__self_class__
属性设置为None
并且不能进行查找:
>>> super(Spam)
<super: <class 'Spam'>, NULL>
>>> super(Spam).__self__ is None
True
>>> super(Spam).__self_class__ is None
True
>>> super(Spam).bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'super' object has no attribute 'bar'
该对象确实支持描述符协议,因此您可以像绑定方法一样绑定它:
>>> super(Spam).__get__(spam, Spam)
<super: <class 'Spam'>, <Spam object>>
>>> super(Spam).__get__(spam, Spam).bar()
'bar on Foo'
这意味着您可以将这样的对象存储在一个类中并使用它来遍历父方法:
>>> class Eggs(Spam):
... pass
...
>>> Eggs.parent = super(Eggs)
>>> eggs = Eggs()
>>> eggs.parent
<super: <class 'Eggs'>, <Eggs object>>
>>> eggs.parent.bar()
'bar on Spam'
主要用例是避免每次都使用以下两种参数形式重复该类super()
:
class Foo:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
# class-private attribute so subclasses don’t clobber one another
setattr(cls, f'_{cls.__name__}__parent', super(cls))
def bar(self):
return 'bar on Foo'
class Spam(Foo):
def bar(self):
return 'spammed: ' + self.__parent.bar()
但是在使用类方法时会中断(因为cls.__parent
不会绑定)并且已被 Python 3 所取代super()
,零参数从闭包中获取类:
class Foo:
def bar(self):
return 'bar on Foo'
class Spam(Foo):
def bar(self):
return 'spammed: ' + super().bar()