我使用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表示他们"做同样的事情"
答案 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版本的一部分。