为什么Python 3.x的super()有魔力?


159

在Python 3.x中,super()可以不带参数调用:

class A(object):
    def x(self):
         print("Hey now")

class B(A):
    def x(self):
        super().x()
>>> B().x()
Hey now

为了完成这项工作,需要执行一些编译时的魔术,其结果是以下代码(重新绑定supersuper_)失败了:

super_ = super

class A(object):
    def x(self):
        print("No flipping")

class B(A):
    def x(self):
        super_().x()
>>> B().x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in x
RuntimeError: super(): __class__ cell not found

为什么super()在没有编译器帮助的情况下无法在运行时解析超类?在实际情况下,这种行为或其根本原因可能会咬一个粗心的程序员吗?

...,还有一个附带的问题:Python中是否还有其他函数,方法等示例,可以通过将它们重新绑定为其他名称来破坏它们吗?


6
我让阿明做解释在这一个。这也是另一个好帖子
Games Brainiac

Answers:


216

super()添加了新的魔术行为,以避免违反DRY(请勿重复自己)原理,请参阅PEP 3135。必须通过将其引用为全局来显式地命名该类,这也容易产生与您自己发现的相同的重新绑定问题super()

class Foo(Bar):
    def baz(self):
        return super(Foo, self).baz() + 42

Spam = Foo
Foo = something_else()

Spam().baz()  # liable to blow up

对于使用类装饰器的情况也是如此,其中装饰器返回一个新对象,该对象重新绑定了类名:

@class_decorator_returning_new_class
class Foo(Bar):
    def baz(self):
        # Now `Foo` is a *different class*
        return super(Foo, self).baz() + 42

魔术 super() __class__单元使您可以访问原始类对象,从而很好地避开了这些问题。

PEP由Guido开始,他最初设想super成为关键字,并且使用单元格查找当前班级的想法也是他的想法。当然,使其成为关键字的想法是PEP初稿的一部分

但是,实际上是Guido自己才放弃了“太不可思议”的关键字想法,而是提出了当前的实现方式。他预计使用其他名称super()可能会出现问题

我的补丁使用了一种中间解决方案:假定您__class__ 每次使用名为的变量时都需要使用该解决方案'super'。因此,如果你(全局)重新命名super,以supper和使用supper,但不能super,也不会不带参数的工作(但它仍然可以工作,如果你通过它要么 __class__或实际的类对象); 如果您有一个名为的不相关变量super,那么一切都会起作用,但是该方法将使用稍慢一些的用于单元变量的调用路径。

因此,最终,Guido自己宣布使用super关键字感觉不正确,并且提供一个魔术__class__单元是可以接受的折衷方案。

我同意该实现的神奇的,隐式的行为有些令人惊讶,但它super()是该语言中使用最广泛的功能之一。只需看看 Internet上所有被误用super(type(self), self)super(self.__class__, self)调用的内容;如果从派生类调用了任何代码,最终都会遇到无限递归异常。至少super(),没有参数的简化调用避免了这种情况问题。

至于更名super_; 刚才提到__class__您的方法,以及它会重新工作。如果您在方法中引用super __class__名称,则会创建该单元格:

>>> super_ = super
>>> class A(object):
...     def x(self):
...         print("No flipping")
... 
>>> class B(A):
...     def x(self):
...         __class__  # just referencing it is enough
...         super_().x()
... 
>>> B().x()
No flipping

1
写得好。它仍然像泥一样清澈。您说的是super()等同于自动实例化的函数def super(of_class=magic __class__),就像self.super(); def super(self): return self.__class__
Charles Merriam 2014年

17
@CharlesMerriam:这篇文章不是关于super()没有参数如何工作的;它主要处理其存在的原因super()在类方法中,等效于super(ReferenceToClassMethodIsBeingDefinedIn, self)ReferenceToClassMethodIsBeingDefinedIn是在编译时确定的__class__,并以名为的闭包附加到该方法,并且super()将在运行时从调用帧中查找两者。但是您实际上不需要了解所有这一切。
马丁·彼得斯

1
@CharlesMerriam:但是super()距离自动实例化功能还差得远。
马丁·彼得斯

1
@ chris.leonard:关键句是如果您使用super()或__class__在method中使用,则会创建该单元格。您super在函数中使用了名称。编译器看到了并添加了__class__闭包。
马丁·彼得斯

4
@Alexey:这不够。type(self)给出当前类型,该类型与在其上定义方法的类型不同。因此,一类Foo与方法baz的需求super(Foo, self).baz(),因为它可以被继承的class Ham(Foo):,在这一点type(self)Hamsuper(type(self), self).baz()会给你一个无限循环。请参阅我在答案中链接到的文章:在派生类中调用super()时,可以传递self .__ class__吗?
马丁·彼得斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.