为什么Python 3.x的超级()魔术?

Zer*_*eus 153 python super python-3.x

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

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

class B(A):
    def x(self):
        super().x()
Run Code Online (Sandbox Code Playgroud)
>>> B().x()
Hey now
Run Code Online (Sandbox Code Playgroud)

为了使这项工作,一些编译时间魔法进行,其中的一个后果是,下面的代码(重新绑定supersuper_)失败:

super_ = super

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

class B(A):
    def x(self):
        super_().x()
Run Code Online (Sandbox Code Playgroud)
>>> 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
Run Code Online (Sandbox Code Playgroud)

super()如果没有编译器的帮助,为什么无法在运行时解析超类?是否存在这种行为或其潜在原因可能会让一个不知情的程序员陷入困境的实际情况?

...并且,作为一个附带问题:在Python中是否有任何其他函数,方法等示例可以通过将它们重新绑定到不同的名称来打破?

Mar*_*ers 210

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
Run Code Online (Sandbox Code Playgroud)

这同样适用于使用类装饰器,其中装饰器返回一个新对象,该对象重新绑定类名:

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

神奇的super() __class__单元格允许您访问原始类对象,从而很好地回避了这些问题.

PEP由Guido 开始super,Guido 最初设想成为关键词,并且使用单元格查找当前课程的想法也是他的.当然,将其作为关键字的想法是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
Run Code Online (Sandbox Code Playgroud)

  • @CharlesMerriam:这篇文章不是关于没有参数的`super()`如何工作; 它主要涉及*为什么*它存在.`super()`,在类方法中,相当于`super(ReferenceToClassMethodIsBeingDefinedIn,self)`,其中`ReferenceToClassMethodIsBeingDefinedIn`在编译时确定,作为名为`__class__`的闭包附加到方法,并且`super() `将在运行时从调用帧中查找.但你实际上并不需要知道这一切. (16认同)
  • @Alexey:**不够**.`type(self)`给出**current**类型,它与定义方法的类型不同.因此,使用方法`baz`的类`Foo`需要`super(Foo,self).baz()`,因为它可以被子类化为`class Ham(Foo):`,此时`type(self)`是`Ham`和`super(type(self),self).baz()`会给你一个无限循环.在我的回答中看到我链接到的帖子:[在派生类中调用super()时,我可以传入self.\ _\_ _ class\_\_ _?](// stackoverflow.com/q/18208683) (4认同)