super() 和显式 super(Cl,self) (带有 __slots__ 和 attrs)有什么区别

Nio*_*bos 4 python super python-3.x python-attrs

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

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()
Run Code Online (Sandbox Code Playgroud)

我得到:

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

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

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

Mar*_*ers 5

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
Run Code Online (Sandbox Code Playgroud)

该闭包允许super()在没有参数的情况下工作(self参数取自本地名称空间)。

但是,当您指定要使用时,attr会生成一个新的类对象__slots__;您无法在事后向类添加插槽,因此会创建一个新的类对象来替换您装饰的对象。

附加的闭包meth是原始的预装饰类,而不是与新生成的类相同的类对象:

>>> Derived.meth.__closure__[0].cell_contents
<class '__main__.Derived'>
>>> Derived.meth.__closure__[0].cell_contents is Derived
False
Run Code Online (Sandbox Code Playgroud)

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

我在《Python 3.x 的 super() 为何如此神奇?》中更详细地介绍了super()无参数的工作原理及其原因。

这在跟踪器中被报告为问题#102 ,并通过使用一些黑客技术改变关闭来修复。此修复将成为即将发布的 17.3 版本的一部分。ctypes