为什么 Python 3.x 的 super() 如此神奇?
- 2025-01-17 09:23:00
- admin 原创
- 100
问题描述:
在 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 中是否还有其他函数、方法等的示例,可以通过将它们重新绑定到不同的名称来破坏?
解决方案 1:
添加新的魔法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
这同样适用于使用类装饰器,装饰器返回一个新对象,该新对象重新绑定类名:
@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
扫码咨询,免费领取项目管理大礼包!