【问题标题】:What is the difference between super() and explicit super(Cl,self) (with __slots__ and attrs)super() 和显式 super(Cl,self) 有什么区别(带有 __slots__ 和 attrs)
【发布时间】:2018-02-19 21:39:39
【问题描述】:

我正在使用attrs python 包,结合继承和插槽。我想从派生方法中调用父类的方法。问题演示如下:

import attr

@attr.s(slots=True)
class Base():
    def meth(self):
        print("hello")

@attr.s(slots=True)
class Derived(Base):
    def meth(self):
        super().meth()
        print("world")

d = Derived()
d.meth()

我明白了:

TypeError: super(type, obj): obj 必须是类型的实例或子类型

问题似乎是由 attrs(具有显式 __slots__=() 工作的未装饰类)、插槽(常规 @attr.s-装饰类工作)和普通 super() 调用(super(Derived, self) 工作)的组合触发的。

我想了解super() 与显式super(Derived, self) 版本的行为有何不同,因为documentation 表示它们“做同样的事情”

【问题讨论】:

标签: python python-3.x super python-attrs


【解决方案1】:

super() 通常依赖编译器提供一个__class__ 闭包单元,该闭包单元绑定到派生方法的类对象。闭包是在您在方法中使用名称 super() 时创建的(或者如果您使用了 __class__):

>>> class Foo(object):
...     def bar(self):
...         super()  # just using super or __class__ is enough
...
>>> Foo.bar.__closure__[0].cell_contents
<class '__main__.Foo'>
>>> Foo.bar.__closure__[0].cell_contents is Foo
True

该闭包让super() 无需参数即可工作(self 参数取自本地命名空间)。

然而,attr 会在您指定要使用__slots__ 时生成一个新的类对象;事后你不能给一个类添加槽,所以一个new class object is created 来替换你装饰的那个。

meth 附加的闭包是原来的预装饰类,与新生成的类不是同一个类对象:

>>> Derived.meth.__closure__[0].cell_contents
<class '__main__.Derived'>
>>> Derived.meth.__closure__[0].cell_contents is Derived
False

这打破了super() 的期望,使得无法使用 0 参数变体。 super(Derived, self) 变体在调用时显式查找名称 Derived 作为全局变量,找到新生成的类,因此可以工作。

我在Why is Python 3.x's super() magic? 中详细介绍了super() 不带参数的工作原理以及原因

这在跟踪器中被报告为issue #102,并且通过使用ctypes hackery 更改闭包来修复。此修复程序将成为即将发布的 17.3 版本的一部分。

【讨论】:

  • 显式版本有效,因为它只在运行代码而不是生成类时评估Derived
  • @Niobos:确切地说,您明确命名为Derived,每次调用该方法时都会将其作为全局查找,并且绑定到新生成的类。
猜你喜欢
  • 1970-01-01
  • 2019-04-26
  • 1970-01-01
  • 1970-01-01
  • 2017-07-13
  • 2020-01-16
  • 2018-11-05
  • 2019-06-07
  • 1970-01-01
相关资源
最近更新 更多