27

在阅读 Python 文档时super(),我偶然发现了以下语句:

如果省略第二个参数,则返回的超级对象是未绑定的。

“未绑定”是什么意思以及如何super()与一个参数一起使用?

4

1 回答 1

31

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()
于 2015-05-12T12:04:08.943 回答