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

Posted

技术标签:

【中文标题】为啥 Python 3.x 的 super() 有魔力?【英文标题】:Why is Python 3.x's super() magic?为什么 Python 3.x 的 super() 有魔力? 【发布时间】:2013-11-05 15:38:11 【问题描述】:

在 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

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

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 中是否有任何其他函数、方法等示例可以通过将它们重新绑定到不同的名称来破坏?

【问题讨论】:

我会让 Armin 解释这个one。这也是另一个好post 相关:***.com/q/36993577/674039 【参考方案1】:

添加了新的魔法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 由 initially envisioned super becoming a keyword 的 Guido 启动,并提出了使用单元格查找当前班级 was also his 的想法。当然,将其作为关键字的想法是 first draft of the PEP 的一部分。

然而,实际上是 Guido 本人随后 stepped away from the keyword idea as 'too magical' 提出了当前的实施方案。他anticipated that using a different name for super() could be a problem:

我的补丁使用了一个中间解决方案:它假设你需要__class__ 每当您使用名为'super' 的变量时。因此,如果您(全球) 将super 重命名为supper 并使用supper 而不是super,它不会工作 没有参数(但如果你传递它,它仍然可以工作 __class__ 或实际的类对象);如果你有一个不相关的 名为super 的变量,一切正常,但方法将使用 用于单元格变量的调用路径稍慢。

所以,最后是 Guido 自己宣称使用 super 关键字感觉不对,而提供神奇的 __class__ 单元格是可以接受的妥协。

我同意实现的神奇、隐式行为有点令人惊讶,但super() 是该语言中应用最多的函数之一。只需看看互联网上发现的所有误用的super(type(self), self)super(self.__class__, self) 调用;如果曾经从派生类you'd end up with an infinite recursion exception 调用过任何代码。至少,没有参数的简化 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

【讨论】:

好文章。然而,它仍然像泥一样清澈。你是说 super() 相当于一个自动实例化的函数,比如 def super(of_class=magic __class__) 有点像 self.super(); def super(self): return self.__class__? @CharlesMerriam:这篇文章不是关于没有参数的super() 是如何工作的;它主要处理它存在的为什么super(),在类方法中,等价于super(ReferenceToClassMethodIsBeingDefinedIn, self),其中ReferenceToClassMethodIsBeingDefinedIn是在编译时确定的,作为名为@9​​87654364@的闭包附加到方法上,super()将从调用框架中查找两者在运行时。但你实际上并不需要知道这一切。 @CharlesMerriam: 但是super() 远不是一个自动实例化的函数,不。 @chris.leonard:关键句是如果你使用 super() 或在你的方法中使用__class__,则创建单元格。您在函数中使用了名称 super。编译器看到并添加 __class__ 闭包。 @Alexey:这还不够type(self) 给出了 current 类型,它与定义方法的类型不同。所以类Foo 和方法baz 需要super(Foo, self).baz(),因为它可以被子类化为class Ham(Foo):,此时type(self)Hamsuper(type(self), self).baz() 会给你一个无限循环。请参阅我在回答中链接到的帖子:When calling super() in a derived class, can I pass in self.__class__?

以上是关于为啥 Python 3.x 的 super() 有魔力?的主要内容,如果未能解决你的问题,请参考以下文章

为啥允许在 Python 中使用 super 从 __init__ 返回一个值?

为啥python有两个不同的版本,并且语法规范啥的都不相通呢?

为啥 ProcessPoolExecutor 和 Pool 在调用 super() 时会崩溃?

为啥在 Python 3.x 的循环中使用 list() 会返回内存错误?

为啥react的组件要super

为啥react的组件要super