Python 3。x, super()可以不带参数调用:
1 2 3 4 5 6 7
| class A(object):
def x(self):
print("Hey now")
class B(A):
def x(self):
super().x() |
为了实现这一功能,执行了一些编译时魔法,其结果之一是以下代码(将super重新绑定到super_)失败:
1 2 3 4 5 6 7 8 9
| super_ = super
class A(object):
def x(self):
print("No flipping")
class B(A):
def x(self):
super_().x() |
1 2 3 4 5
| >>> 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中是否还有其他函数、方法等的例子可以通过将它们重新绑定到不同的名称来打破它们?
- 我让阿明来解释这个。这也是另一篇好文章
- 相关:stackoverflow.com/q/36993577/674039
添加新的magic super()行为是为了避免违反D.R.Y.(不要重复自己)原则,见PEP 3135。必须显式地将类引用为全局类来命名也容易出现与super()本身相同的重新绑定问题:
1 2 3 4 5 6 7 8
| class Foo(Bar):
def baz(self):
return super(Foo, self).baz() + 42
Spam = Foo
Foo = something_else()
Spam().baz() # liable to blow up |
同样的情况也适用于使用类修饰器,修饰器返回一个新对象,该对象将重新绑定类名:
1 2 3 4 5
| @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初稿的一部分。
然而,实际上是圭多自己放弃了关键字"太神奇"的想法,转而提出了当前的实现。他预计为super()使用不同的名称可能是一个问题:
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()是该语言中应用最广泛的函数之一。只要看看所有误用的super(type(self), self)或super(self.__class__, self)调用在互联网上发现;如果这些代码中有任何一个是从派生类中调用的,那么最终会出现无限递归异常。至少,没有参数的简化super()调用避免了这个问题。
重新命名的super_;只要在你的方法中引用__class__,它就会再次运行。如果在方法中引用super或__class__名称,则创建单元格:
1 2 3 4 5 6 7 8 9 10 11 12
| >>> 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在编译时确定,作为一个名为__class__的闭包附加到方法上,并且super()将在运行时从调用框架中查找这两个闭包。但你不需要知道所有这些。
- @CharlesMerriam:但是super()离自动实例化函数还差得很远,不是。
- 我刚刚在stackoverflow.com/q/48691652/1236579上打开了一个类似的问题,关闭时是一个完全相同的副本。但是,我不明白为什么它会被认为是一个副本,因为我在更新和评论中提到了这个帖子。可能是我不明白什么。有没有人能同情地阅读我的问题、评论和更新,或者告诉我我错过了什么,或者验证它不应该真的是一个骗局?
- @chris。leonard:关键句是,如果在方法中使用super()或__class__,则创建单元格。您在函数中使用了名称super。编译器看到这一点并添加__class__闭包。
- @MartijnPieters:我明白了。我一点也不了解这个细胞。现在我知道该读些什么,可以取得进步。事后看来,这是一个不必要的问题,谢谢你们所有人的帮助。
- "必须通过引用类作为全局变量来显式地命名该类"——但是为什么呢?像在其他任何地方一样,使用type(self)就足够了。
- 这还不够。type(self)给出当前类型,该类型与方法定义的类型不同。因此,具有方法baz的类Foo需要super(Foo, self).baz(),因为它可以被子类化为class Ham(Foo):,此时type(self)是Ham,而super(type(self), self).baz()将给您一个无限循环。查看我在回答中链接到的帖子:当在派生类中调用super()时,我可以传入self._ class__吗?
- 这在我看来是可以的,这就是继承的作用。在这个例子中,super(type(self), self).baz()不正是您想要调用的吗?
- 只有当你不子类化时,它才看起来足够。这是骗人的,而且是一个常见的错误,因为它只在子类时才会爆炸。尝试我链接的另一篇文章中的代码,您将看到为什么不能使用type(self)。