python - 为什么类方法的 super 需要第二个参数?

标签 python python-3.x inheritance python-2.x class-method

这按预期工作:

>>> class Foo(object):
...   @classmethod
...   def hello(cls):
...     print 'hello, foo'
... 
>>> class Bar(Foo):
...   @classmethod
...   def hello(cls):
...     print 'hello, bar'
...     super(Bar, cls).hello()
... 
>>> b = Bar()
>>> b.hello()
hello, bar
hello, foo

我也可以显式调用基类:

>>> class Bar(Foo):
...   @classmethod
...   def hello(cls):
...     print 'hello, bar'
...     Foo.hello()
... 
>>> b = Bar()
>>> b.hello()
hello, bar
hello, foo

我想知道为什么我不能省略 super 的第一个参数,如下所示:

>>> class Bar(Foo):
...   @classmethod
...   def hello(cls):
...     print 'hello, bar'
...     super(Bar).hello()
... 
>>> b = Bar()
>>> b.hello()
hello, bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in hello
AttributeError: 'super' object has no attribute 'hello'

当没有第二个参数的 super 调用的结果似乎是父类(super class)型中的类类型时:

>>> class Bar(Foo):
...   @classmethod
...   def hello(cls):
...     print Foo, type(Foo)
...     print super(Bar), type(super(Bar))
...     print cls, type(cls)
... 
>>> b = Bar()
>>> b.hello()
<class '__main__.Foo'> <type 'type'>
<super: <class 'Bar'>, NULL> <type 'super'>
<class '__main__.Bar'> <type 'type'>

我想我只是想知道这里的设计。为什么我需要将类对象传递到 super 调用中以获取对基类类型 Foo 的引用?对于普通方法,将 self 传递给函数是有意义的,因为它需要将基类类型绑定(bind)到该类的实际实例。但是类方法不需要类的特定实例。

编辑: 我在 Python 3.2 中遇到的错误与我在 2.7 中针对 super(Bar).hello() 时遇到的错误相同。但是,我可以简单地执行 super().hello() 并且工作正常。

最佳答案

super() 返回 descriptor ,并且需要两项:

  • 搜索类层次结构的起点。
  • 绑定(bind)返回方法的参数。

对于双参数(和隐式零参数 *)的情况,第二个参数用于绑定(bind),但是如果你不传入第二个参数,super() 不能调用描述符协议(protocol)来绑定(bind)返回的函数、类方法、属性或其他描述符。 classmethods 仍然是描述符并且是绑定(bind)的;绑定(bind)到类而不是实例,但是 super() 不知道描述符将如何使用您绑定(bind)的上下文。

super() 不应该也不能知道您正在查找类方法而不是常规方法;类方法仅与常规方法不同,因为它们的 .__get__() 方法行为不同。

为什么要绑定(bind)类方法?因为当您子类化 Foo 覆盖 .hello() 时,调用 Bar.hello() 会调用Foo.__dict__['hello'] 函数,将其绑定(bind)到 Bar 并且您对 hello(cls) 的第一个参数将是该子类,而不是Foo.

如果没有第二个参数,super() 返回一个未绑定(bind)的对象,稍后可以手动绑定(bind)。您可以使用 super() 实例提供的 .__get__() 方法自己进行绑定(bind):

class Bar(Foo):
    @classmethod
    def hello(cls):
        print 'hello, bar'
        super(Bar).__get__(cls, None).hello()

super().__get__() 在没有上下文的实例上有效地返回一个带有上下文集的新 super() 实例。在具有上下文 .__get__() 的实例上只返回 self;它已经绑定(bind)了。


* 在 Python 3 中,从绑定(bind)方法内部调用不带参数的 super() 将使用调用框架隐式地发现类型和绑定(bind)对象是什么,因此在这种情况下您不再需要显式传递类型和对象参数。为此,Python 3 实际上向方法中添加了一个隐式的 __class__ 闭包变量。参见 PEP 3135Why is Python 3.x's super() magic?

关于python - 为什么类方法的 super 需要第二个参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17575074/

相关文章:

python - 我应该使用事件、信号量、锁、条件或其组合来管理安全退出我的多线程 Python 程序吗?

python - Python 中的继承使得所有基函数都被调用

python - 我们能否通过接受(或忽略)新功能使 ML 模型(pickle 文件)更加健壮?

python - csv 文件中的字符串转换为 float

python - 更多Pythonic/更好的方式来编写这个?

javascript - 如何从 JSON 继承获取 JavaScript 对象?

java - 限制子类中的函数调用

python - 来自一组坐标的边界点

python - kmeans 簇数与 k 值不匹配

python - 当我从空闲状态导入 `http.server` 时它可以工作,但是当我运行具有 `import http.server` 的 python 文件时出现错误