【问题标题】:Why does a classmethod's super need a second argument?为什么类方法的 super 需要第二个参数?
【发布时间】:2013-07-08 15:28:05
【问题描述】:

这按预期工作:

>>> 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'>

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

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

【问题讨论】:

  • 在 python 3.x 中,他们修复了很多超级调用......在 python2x 中,他们只是没有考虑那么多(我的猜测......)无论如何我认为这将结束关闭为“为什么”问题通常是......
  • 您可能会发现这很有用:stackoverflow.com/questions/11354786/…
  • @JoranBeasley meh,在此之前我已经问过几个为什么类型的问题还没有结束。
  • @JoranBeasley:对于许多这样的问题,当涉及到 Python 时,原因有据可查,使得这些问题可以完美回答。 Python 3 添加了一个隐式的__class__ 范围变量,因此您可以省略参数,super() 将检查调用框架以获取类型和绑定参数(selfcls)。
  • @jterrace:因为只有 没有 参数的版本才会在周围范围内查找要绑定的类型和对象。

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


【解决方案1】:

super() 返回一个descriptor,需要两项:

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

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

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

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

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

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

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


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

【讨论】:

    猜你喜欢
    • 2011-11-19
    • 2018-06-26
    • 1970-01-01
    • 2018-10-10
    • 2016-05-20
    • 2015-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多