为什么 Python 3. x 的 super()魔法如此神奇?

在 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 中还有其他的函数、方法等例子可以通过将它们重新绑定到一个不同的名字来破解吗?

48132 次浏览

为了避免违反 DRY (不要重复自己)原则,新增加了神奇的 super()行为,参见 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成为一个关键字,并提出了使用单元格查找当前类 也是他的的想法。当然,使其成为关键字的想法是 人口动态伙伴关系计划初稿的一部分。

然而,事实上是圭多自己当时提出了目前的实施方案。他提出了 预计为 super()使用不同的名称可能是一个问题:

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

所以,最后,是圭多自己宣称使用 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