2

我正在使用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()

我得到:

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

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

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

4

1 回答 1

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__;事后您不能将插槽添加到类中,因此会创建一个新的类对象来替换您装饰的对象。

附加的闭包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作为全局名称,找到新生成的类,因此有效。

在Python 3.x 的 super()super()为何具有魔力?

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

于 2017-09-11T15:37:57.393 回答