【问题标题】:Order of base classes and super() usage in multiple inheritance多重继承中基类的顺序和 super() 的使用
【发布时间】:2014-11-02 17:54:57
【问题描述】:

您能帮我理解这两种情况的区别吗?

class B1:
    def f(self):
        super().temp()

class B2:
    def temp(self):
        print("B2")

class A(B1, B2):
    pass

A().f()

它打印"B2"

如果我们切换B1B2

class A(B2, B1):
    pass

A().f()

我收到AttributeError: 'super' object has no attribute 'temp'

【问题讨论】:

  • 什么让你困惑? B2 没有实现f,所以如果先在那里查找(在第二种情况下)它就找不到。
  • 我猜 OP 不明白为什么 A 类的参数顺序在这里很重要,或者排序有什么影响。

标签: python python-3.x multiple-inheritance super


【解决方案1】:

Python 使用名为C3 linearization 的东西来决定基类的顺序:“方法解析顺序”。非正式地说,这基本上有两个部分:

  • 路径必须层次结构绝不能从一个类下降到它的超类,即使是间接的。因此,不允许循环,issubclass(X, Y) and issubclass(Y, Z) 暗示 issubclass(X, Z)

  • 上述规则未强制时的顺序是按到最高级类的步数排序(步数越少意味着在链中越早),然后的顺序是类列表中的类(列表中的较早表示链中的较早)。

这里的层次结构是:

    A
   / \
  /   \
B1     B2   # Possibly switched
  \   /
   \ /
  object

在第一种情况下,C3线性化后的顺序是

  super    super    super
A   →   B1   →   B2   →   object

我们可以通过以下方式找到:

A.mro()
#>>> [<class 'A'>, <class 'B1'>, <class 'B2'>, <class 'object'>]

所以super() 调用将解析为:

class A(B1, B2):
    pass

class B1:
    def f(self):
        # super() proxies the next link in the chain,
        # which is B2. It implicitly passes self along.
        B2.temp(self)

class B2:
    def temp(self):
        print("B2")

所以调用A().f() 尝试:

  • f 在实例上吗?不,所以
  • f 在头等舱A 上吗?不,所以
  • f 在下一堂课B1 上吗?是的!

然后调用B1.f,这调用B2.temp(self),它检查:

  • f 在课堂上吗,B2?是的!

它被调用,打印B2

在第二种情况下,我们有

  super    super    super
A   →   B2   →   B1   →   object

所以解决了

所以super() 调用将解析为:

class A(B2, B2):
    pass

class B2:
    def temp(self):
        print("B2")

class B1:
    def f(self):
        # super() proxies the next link in the chain,
        # which is B2. It implicitly passes self along.
        object.temp(self)
  • f 在实例上吗?不,所以
  • f 在头等舱,A?不,所以
  • f 在下一堂课B2 上吗?不,所以
  • f 在下一堂课B1 上吗?是的!

所以B1.f 被调用,这调用object.temp(self),它检查:

  • f 在课堂上吗,object?不,
  • 没有超类,所以找不到属性。
  • 提高AttributeError("{!r} object has no attribute {!r}".format(instance, attribute_name))

【讨论】:

    【解决方案2】:

    区别只是在这两种情况下,A 类的 MRO 中的类顺序:

    class A1(B1, B2):
        pass
    
    class A2(B2, B1):
        pass
    
    print(A1.mro())
    print(A2.mro())
    

    返回:

    [<class '__main__.A1'>, <class '__main__.B1'>, <class '__main__.B2'>, <class 'object'>]
    

    [<class '__main__.A2'>, <class '__main__.B2'>, <class '__main__.B1'>, <class 'object'>]
    

    现在,当您调用A1.f()A2.F() 时,属性在B1 中找到,并且您在此处调用super().temp(),这意味着在找到的下一个类上调用temp()(或转到旁边的类直到在MRO 中找不到temp 等等..)。

    A2 的情况下,下一个也是唯一的类是object,它没有temp() 方法,因此会引发错误。

    如果是A1,在B1 之后的下一个类是B2,它有一个temp() 方法,因此不会引发错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-30
      • 2016-02-25
      • 1970-01-01
      • 2022-01-20
      • 2016-08-09
      • 2018-11-06
      • 2021-11-23
      • 2020-07-24
      相关资源
      最近更新 更多