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

2024-05-20 05:46:52 发布

您现在位置:Python中文网/ 问答频道 /正文

在Python 3.x中,^{}可以不带参数调用:

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


Tags: inself示例参数objectdefstdinline
1条回答
网友
1楼 · 发布于 2024-05-20 05:46:52

新的魔法行为是为了避免违反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

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

@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发起的,他是initially envisioned ^{} becoming a keyword,并且是使用一个单元格来查找当前类was also his。当然,将其作为关键字的想法是first draft of the PEP的一部分。

然而,事实上是Guido自己当时提出了当前的实现。他anticipated that using a different name for ^{} could be a problem

My patch uses an intermediate solution: it assumes you need __class__ whenever you use a variable named 'super'. Thus, if you (globally) rename super to supper and use supper but not super, it won't work without arguments (but it will still work if you pass it either __class__ or the actual class object); if you have an unrelated variable named super, things will work but the method will use the slightly slower call path used for cell variables.

因此,最终,是圭多自己宣称使用super关键字感觉不对,而提供一个神奇的__class__细胞是一个可以接受的折衷方案。

我同意实现的神奇、隐式行为有些令人惊讶,但是super()是语言中应用最错误的函数之一。只要看看Internet上发现的所有误用的^{}^{}调用;如果有任何代码是从派生类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

相关问题 更多 >