12

这按预期工作:

>>> 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没有第二个参数的调用结果似乎是超类型中的类类型时:

>>> 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的,因为它需要将基类类型绑定到类的实际实例。但是类方法不需要类的特定实例。

编辑:我在 Python 3.2 中遇到与上面 2.7 中相同的错误super(Bar).hello()。但是,我可以简单地做super().hello(),而且效果很好。

4

1 回答 1

15

super()返回一个描述符,并且需要两项:

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

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

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

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

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

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

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


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

于 2013-07-10T16:02:45.777 回答