关于Python 3.x的MRO和”super()”的问题。

huangapple go评论65阅读模式
英文:

question regarding python 3.x MRO and "super()"

问题

这段代码显示错误
AttributeError: 'super' object has no attribute 'method'

当在类C中包括"super().method"时,为什么这个程序会显示错误,
而当在类C中删除"super().method"时,为什么同样的程序不显示错误

要知道为什么,需要了解Python中super()的工作原理以及类继承的方法解析顺序(Method Resolution Order,MRO)。
super()用于调用父类的方法,但在这里,它可能引发了错误,因为它依赖于类的MRO,它决定了方法解析的顺序。在类C中使用super()时,它会按照MRO查找下一个类,但如果没有下一个类,就会引发AttributeError。

在类C中删除"super().method"可能不会引发错误,因为它不再尝试调用下一个类的同名方法。这会影响到程序的行为,具体取决于你的需求和类的继承结构。

要更深入地了解问题的根本原因,需要查看类的MRO和super()的工作方式,以确切地解释为什么这两种情况会产生不同的结果。

英文:

This code shows error:
AttributeError: 'super' object has no attribute 'method'

this same code does not shows error when "super.method()" is removed in class C.
Why?

class A(object):
    def method(self):
        print("\n class A method")
        super().method()

class B(object):
    def method(self):
        print("\n class B method")
        super().method()

class C(object):
    def method(self):
        print("\n class C method")
        super().method()
        

class X(A, B):
    def method(self):
        print("\n class X method")
        super().method()

class Y(B, C):
    def method(self):
        print("\n class Y method")
        super().method()

class P(X, Y, C):
    def method(self):
        print("\n class P method")
        super().method()

p = P()
p.method()

print(P.mro())

I want to know, why this program is showing errors when "super().method" is included in class C,
and why this same program is NOT SHOWING ERROR, when "super().method" is removed in class C?

答案1

得分: 1

这不是一个错误,而是与多重继承和 super 一起使用的特性。让我逐个回答您的问题:

> 为什么在类 C 中包含 "super().method" 时,程序会显示错误?

原因是基类 object 没有名为 method 的方法。当调用 C().method() 或简单地调用 object().method() 时,您将看到相同的异常。所以我认为这是相当正常的。

> 为什么在类 C 中删除 "super().method" 时,同样的程序不会显示错误?

这是 Python 方法解析顺序(MRO)的魔力。如果您执行

print(P.mro())

您应该会看到

[__main__.P,
 __main__.X,
 __main__.A,
 __main__.Y,
 __main__.B,
 __main__.C,
 object]

在这里,您可以看到 super 调用不会一直调用类的所有父类,直到继承树的顶部,然后再取下一个类。相反,它们将根据它们的 "generation" 调用它们。

看下面的例子:

class Base:
    def method(self):
        print("Base")

class ParentA(Base):
    def method(self):
        print('Parent A')
        super().method()
        
class ParentB(Base):
    def method(self):
        print("Parent B")
        
class Child(ParentA, ParentB):
    def method(self):
        print("Child")
        super().method()

print(Child.mro())
Child().method()

这将给出以下输出:

[<class '__main__.Child'>, <class '__main__.ParentA'>, <class '__main__.ParentB'>, <class '__main__.Base'>, <class 'object'>]
Child
Parent A
Parent B

您不会看到 Base 这一行,因为在 ParentA 中的 super 被忽略,因为存在与之共享相同基类的 ParentB

但是,如果您交换继承关系,您将看到打印出 Base 这一行。
简而言之,要遵循 MRO,但如果一代中的最后一个类没有调用 super,链条就会停止。

我同意这并不直观,但在实践中,这很少是一个问题,或者如果您必须考虑 MRO 才能理解代码,那么这将是重构的一个很好候选项。

要了解 MRO,我建议阅读 https://en.wikipedia.org/wiki/C3_linearization 或在 Stack Overflow 上阅读其他相关问题。

英文:

This is not a bug, but a feature of working with multiple inheritance and super. Let me answer your questions one by one:

> why this program is showing errors when "super().method" is included in class C?

The reason is that the base class object does not have a method called method. You will see the same exception when calling C().method() or simply object().method(). So I guess this is quite expected.

> why this same program is NOT SHOWING ERROR, when "super().method" is removed in class C?

This is the magic of python's method resolution order (MRO). If you execute

print(P.mro())

you should see

[__main__.P,
 __main__.X,
 __main__.A,
 __main__.Y,
 __main__.B,
 __main__.C,
 object]

Here, you see that a super call will not call all parents of a class until the top of the inheritance tree and then take the next class. Instead, it will call them according to their "generation".

Look at the following example:

class Base:
    def method(self):
        print(&quot;Base&quot;)

class ParentA(Base):
    def method(self):
        print(&#39;Parent A&#39;)
        super().method()
        
class ParentB(Base):
    def method(self):
        print(&quot;Parent B&quot;)
        
class Child(ParentA, ParentB):
    def method(self):
        print(&quot;Child&quot;)
        super().method()

print(Child.mro())
Child().method()

Which will give you

[&lt;class &#39;__main__.Child&#39;&gt;, &lt;class &#39;__main__.ParentA&#39;&gt;, &lt;class &#39;__main__.ParentB&#39;&gt;, &lt;class &#39;__main__.Base&#39;&gt;, &lt;class &#39;object&#39;&gt;]
Child
Parent A
Parent B

You do not see the line Base, because super in ParentA is ignored due to the presence of ParentB which shares the same base.

However, if you swap the inheritance, you will see the line Base printed.
In short, follow the MRO, but if the last class in a generation does not call super, the chain stops.

I agree that this is not intuitive, but in practice this is rarely a problem, or if you have to think about the MRO to understand the code, it would be a good candidate for refactoring.

To understand the MRO, I recommend to read https://en.wikipedia.org/wiki/C3_linearization or other related questions at SO.

huangapple
  • 本文由 发表于 2023年1月6日 14:48:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/75027798.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定