2013-07-10 70 views
7

可正常工作:为什么classmethod的super需要第二个参数?

>>> 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传递给函数是有意义的,因为它需要将基类类型绑定到该类的实际实例。但是classmethod不需要该类的特定实例。

编辑: 我得到的Python 3.2相同的错误,因为我在上面2.7 super(Bar).hello()做。不过,我可以简单地做super().hello(),这工作正常。

+0

在python 3.x中它们修复了超级调用...在python2x中,它们只是没有认为它通过那么多(我的猜测是......)无论如何,我认为这将最终关闭如“为什么”的问题通常是... –

+0

您可能会发现这个有用:http://stackoverflow.com/questions/11354786/super-confusing-python-multiple-inheritance-super?rq=1 – mdscruggs

+0

@JoranBeasley咩,我已经问过几个为什么类型的问题还没有结束。 – jterrace

回答

7

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

  • 一个出发,从中搜索类层次结构点。
  • 参数绑定返回的方法。

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

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

为什么要绑定类方法?因为当你继承Foo但做覆盖.hello(),称Bar.hello()调用Foo.__dict__['hello']功能,它结合Bar和你的第一个参数hello(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实际上为方法增加了一个隐含的闭包变量。请参阅PEP 3135Why is Python 3.x's super() magic?

相关问题