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

时间:2017-09-11 15:26:37

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

我使用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表示他们"做同样的事情"

1 个答案:

答案 0 :(得分: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版本的一部分。