为什么Python 3.x中的super()这么神奇?

162

在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
我会让 Armin 解释一下这个链接的内容。这是另一篇很好的文章 - Games Brainiac
请返回已翻译的文本。 - wim
1个回答

226
新增了一个名为super()的神奇行为,以避免违反 D.R.Y.(不要重复自己)原则,请参阅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()是语言中最被误用的函数之一。只需看看互联网上所有错误应用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
17
这篇文章并不是关于super()在没有参数的情况下如何工作的;它主要涉及到super()存在的原因。在类方法中,super()相当于super(正在被定义的类方法的引用, self),其中正在被定义的类方法的引用在编译时确定,并作为一个名为__class__的闭包附加到该方法上,super()将在运行时从调用帧中查找两者。但实际上你不需要知道所有这些。 - Martijn Pieters
1
@CharlesMerriam:但是super()远远不是自动实例化函数 - Martijn Pieters
4
@Alexey: 这还不够。type(self) 会返回当前的类型,而非方法定义时的类型。因此,如果一个类 Foo 有一个名为 baz 的方法,则需要使用 super(Foo, self).baz(),因为它可能被子类化为 class Ham(Foo):,此时 type(self)Ham,而 super(type(self), self).baz() 会导致无限循环。请参阅我在答案中提供的链接:When calling super() in a derived class, can I pass in self.class? - Martijn Pieters
1
@Alexey:如果您不使用子类,它看起来足够。这是一种欺骗性的做法,并且很容易犯错误,因为只有在您使用子类时才会出现问题。尝试在我链接的另一篇帖子中运行代码,您就会明白为什么不能使用type(self) - Martijn Pieters
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接