为什么 Python 3.x 的 super() 很神奇? [英] Why is Python 3.x's super() magic?

查看:58
本文介绍了为什么 Python 3.x 的 super() 很神奇?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

class A(object):def x(self):打印(嘿现在")B(A)类:def x(self):超级().x()

>>>B().x()嘿现在

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

super_ = superA类(对象):def x(self):打印(不翻转")B(A)类:def x(self):super_().x()

>>>B().x()回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中文件<stdin>",第 3 行,在 x 中运行时错误:super():找不到 __class__ 单元格

为什么 super() 在没有编译器帮助的情况下无法在运行时解析超类?是否存在这种行为或其根本原因可能会咬住粗心的程序员的实际情况?

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

解决方案

添加了新的魔法 super() 行为以避免违反 D.R.Y.(不要重复自己)原则,请参阅 PEP 3135.必须通过将类作为全局引用来显式命名类也容易出现与您在 super() 本身中发现的相同的重新绑定问题:

class Foo(Bar):def baz(自我):返回 super(Foo, self).baz() + 42垃圾邮件 = FooFoo = something_else()Spam().baz() # 容易炸毁

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

@class_decorator_returning_new_class类 Foo(Bar):def baz(自我):# 现在`Foo` 是一个*不同的类*返回 super(Foo, self).baz() + 42

神奇的 super() __class__ 单元让您可以访问原始类对象,从而很好地避开了这些问题.

PEP 由 Guido 发起,他最初设想super 成为关键字,以及使用单元格查找当前类的想法 也是他的.当然,将其作为关键字的想法是 初稿的一部分PEP.

然而,实际上是 Guido 本人走开了从关键字idea为太神奇",改为提出当前的实现.他预计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__ 名称,则会创建单元格:

<预><代码>>>>超级_ = 超级>>>A类(对象):... def x(self):...打印(不翻转")...>>>B(A)类:... def x(self):... __class__ # 引用就够了... super_().x()...>>>B().x()没有翻转

In Python 3.x, super() can be called without arguments:

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

class B(A):
    def x(self):
        super().x()

>>> B().x()
Hey now

In order to make this work, some compile-time magic is performed, one consequence of which is that the following code (which rebinds super to super_) fails:

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

Why is super() unable to resolve the superclass at runtime without assistance from the compiler? Are there practical situations in which this behaviour, or the underlying reason for it, could bite an unwary programmer?

... and, as a side question: are there any other examples in Python of functions, methods etc. which can be broken by rebinding them to a different name?

解决方案

The new magic super() behaviour was added to avoid violating the D.R.Y. (Don't Repeat Yourself) principle, see PEP 3135. Having to explicitly name the class by referencing it as a global is also prone to the same rebinding issues you discovered with super() itself:

class Foo(Bar):
    def baz(self):
        return super(Foo, self).baz() + 42

Spam = Foo
Foo = something_else()

Spam().baz()  # liable to blow up

The same applies to using class decorators where the decorator returns a new object, which rebinds the class name:

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

The magic super() __class__ cell sidesteps these issues nicely by giving you access to the original class object.

The PEP was kicked off by Guido, who initially envisioned super becoming a keyword, and the idea of using a cell to look up the current class was also his. Certainly, the idea to make it a keyword was part of the first draft of the PEP.

However, it was in fact Guido himself who then stepped away from the keyword idea as 'too magical', proposing the current implementation instead. He anticipated that using a different name for super() 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.

So, in the end, it was Guido himself that proclaimed that using a super keyword did not feel right, and that providing a magic __class__ cell was an acceptable compromise.

I agree that the magic, implicit behaviour of the implementation is somewhat surprising, but super() is one of the most mis-applied functions in the language. Just take a look at all the misapplied super(type(self), self) or super(self.__class__, self) invocations found on the Internet; if any of that code was ever called from a derived class you'd end up with an infinite recursion exception. At the very least the simplified super() call, without arguments, avoids that problem.

As for the renamed super_; just reference __class__ in your method as well and it'll work again. The cell is created if you reference either the super or __class__ names in your method:

>>> 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

这篇关于为什么 Python 3.x 的 super() 很神奇?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆