super() 和显式 super(Cl,self) 之间有什么区别(使用 __slots__ 和 attrs)

What is the difference between super() and explicit super(Cl,self) (with __slots__ and attrs)

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

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 must be an instance or subtype of type

问题似乎是由属性(未修饰的 classes 与显式 __slots__=() 工作)、槽(常规 @attr.s-修饰的 classes工作)和普通的 super() 调用(super(Derived, self) 工作)。

我想了解 super() 与显式 super(Derived, self) 版本的行为有何不同,因为 documentation 表示它们 "do the same thing"

super() 通常依赖于编译器提供一个 __class__ 闭包单元,该单元绑定到 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 参数取自本地命名空间)。

但是,当您指定要使用 __slots__ 时,attr 会生成一个 新的 class 对象 ;你不能在事后向 class 添加插槽,因此 new class object is created 会替换你装饰的插槽。

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

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

这打破了 super() 的预期,因此无法使用 0 参数变体。 super(Derived, self) 变体在调用时明确地将名称 Derived 查找为全局名称,找到新生成的 class,如此有效。

我在 Why is Python 3.x's super() magic?

中详细介绍了不带参数的 super() 的工作原理以及原因

这被报告为 issue #102 in the tracker, and fixed by altering the closure with some ctypes hackery。此修复将成为即将发布的 17.3 版本的一部分。