我正在编写一些 Python 代码,看起来它非常适合多重继承,并且我正在阅读有关多重继承的内容,
super()
以确保我正确理解它们的工作原理。
我对
super()
的理解是 - 解释一下 - 它会遍历类的方法解析顺序并返回某种“调度程序”,其中通过 __getattr__
访问方法或属性会访问该方法或属性的“第一个”实现,按照 MRO 的规定。
然而,RealPython 的这个例子让我感到困惑。 (我添加了打印语句,并且编辑了他们的示例以删除所有功能方法,因为我在这里关心的是
super()
如何与 MRO 和各种 __init__
方法一起使用。)
class Rectangle:
def __init__(self, length, width, **kwargs):
print("Running Rectangle.__init__")
self.length = length
self.width = width
super().__init__(**kwargs)
class Square(Rectangle):
def __init__(self, length, **kwargs):
print("Running Square.__init__")
super().__init__(length=length, width=length, **kwargs)
class Triangle:
def __init__(self, base, height, **kwargs):
print("Running Triangle.__init__")
self.base = base
self.height = height
super().__init__(**kwargs)
class RightPyramid(Square, Triangle):
def __init__(self, base, slant_height, **kwargs):
print("Running RightPyramid.__init__")
self.base = base
self.slant_height = slant_height
kwargs["height"] = slant_height
kwargs["length"] = base
super().__init__(base=base, **kwargs)
_ = RightPyramid(10, 5)
我的期望是,因为在 RightPyramid 的继承顺序中 Square 位于 Triangle 之前,所以
super(RightPyramid).__init__
相当于 Square.__init__
。确实:
>>> super(RightPyramid).__init__
<bound method Square.__init__ of <__main__.RightPyramid object at [...]>
但是,当我实际运行此代码来查看打印的内容时:
lux@parabolica:~/Desktop $ python test.py
Running RightPyramid.__init__ # No problem
Running Square.__init__ # Square comes first, makes sense
Running Rectangle.__init__ # Square calls Rectangle, gotcha
Running Triangle.__init__ # What? How?!
这是怎么回事?
Triangle.__init__
怎样才能接到电话?
有趣的是,这实际上完全是我希望在我正在处理的代码中发生的事情 - 将来自多个超类的多个
__init__
方法“混合在一起” - 但我没有看到任何文档或文章已阅读表明 super()
应该以这种方式表现。据我所读, super()
应该只“解析”并调用一个方法;从检查 super(RightPyramid).__init__
来看,似乎这就是正在发生的事情,但从代码的输出来看,Triangle.__init__
显然被调用了......不知何故。
我在这里误解了什么,在哪里可以阅读有关
super()
的此功能的更多信息?
(这似乎是super()
super().<method>()
从MRO中的下一个类调用方法,它不会自动调用所有超类中的方法。仅当这些方法中的每一个也调用
super().<method>()
时,整个 MRO 链才会被处理。这就是为什么它被称为
合作多重继承——它依赖于每个类通过调用super()
进行合作。