Python的多重继承:选择调用哪个super()

64
在Python中,我该如何选择要调用哪个父类的方法?比如我想要调用父类ASDF2的__init__方法。看起来我必须在super()中指定ASDF1?如果我想要调用ASDF3的__init__方法,那么我必须指定ASDF2吗?!

在Python中,如何选择要调用哪个父类的方法?比如我想要调用父类ASDF2的__init__方法。看起来我必须在super()中指定ASDF1?如果我想要调用ASDF3的__init__方法,那么我必须指定ASDF2?!

>>> class ASDF(ASDF1, ASDF2, ASDF3):
...     def __init__(self):
...         super(ASDF1, self).__init__()
            
>>> ASDF()
# ASDF2's __init__ happened

>>> class ASDF(ASDF1, ASDF2, ASDF3):
...     def __init__(self):
...         super(ASDF2, self).__init__()

>>> ASDF()
# ASDF3's __init__ happened

对我来说似乎很荒谬。我做错了什么吗?

4个回答

91

那不是 super() 的作用。Super 基本上会按照特定的顺序选择其父类中的一个(或全部)。如果你只想调用单个父类的方法,请这样做

class ASDF(ASDF1, ASDF2, ASDF3):
    def __init__(self):
        ASDF2.__init__(self)

1
不幸的是,如果__init__引用任何私有方法,这将无法工作。 - Erik Aronesty
2
@ErikAronesty Python中不存在私有方法的概念,您能否澄清一下您的意思? - Nearoo
1
@nearoo 是的,如果一个方法以"__"开头,它的方法名就会被编码并且方法是私有的。当以这种方式调用__init__函数时,它将无法使用私有方法。但是super()可以。 - Erik Aronesty
那么继承的意义在哪里呢?在这种情况下,您不需要继承就可以访问ASDF2。它是一个全局定义。 - user32882
为什么你说它会选择(或全部选择)?它不是总是选择第一个吗? - Benyamin Jafari

12

super 调用方法解决顺序中的下一个方法。在线性继承树中,这将是立即父类中的方法。

在这里,你有三个父类,ASDF1 的下一个 __init__ 方法从 ASDF2 的角度来看。一般来说,安全的做法是将继承列表中的第一个类传递给 super,除非你知道为什么要做其他事情。

所有这些的目的是为了使你不必显式选择要调用哪个 __init__ 方法。这里的问题是为什么你不想调用所有超类的 __init__ 方法。

有大量关于在 Python 中使用 super 的文献资料,我建议你搜索这个主题,并阅读结果。


如果我有一个继承自 threading.ThreadASDF2 的类,那么当我调用 super().__init__() 时,线程将不会被初始化并且会抛出异常:RuntimeError: thread.__init__() not called - iggy12345

10

你正在将 ASDF1(其中一个父类)作为 super() 的第一个参数传递。不要这样做。相反,你应该将 ASDF 作为 super() 的第一个参数。

Python 2.7 documentation for super()

引用 Python 2.7 文档的话,一个典型的超类调用看起来像这样:

class C(B):
    def method(self, arg):
        super(C, self).method(arg)

注意,这里super的第一个参数是C,即当前类,不要将B(父类)传递给super()。
在确定方法解析顺序(MRO)时,super跳过作为其第一个参数传递的类,并开始查看该类的父级和同级。因此,当您将ASDF1作为super()的第一个参数传递时,它跳过了ASDF1并从ASDF2开始搜索。这就是为什么调用了ASDF2的__init__方法。
在Python 3中,您不再需要传递当前类。
class C(B):
    def method(self, arg):
        super().method(arg)    # This does the same thing as:
                               # super(C, self).method(arg)
                               # (Python 3 only)

0
现在在Python3中,只需控制super()在mro中搜索的方法即可。
class _TestA:
    def __init__(self, foo: str):
        self._print = "testA-" + foo


class _TestB:
    def __init__(self, foo: str):
        self._print = "testB-" + foo


class TestAB(_TestA, _TestB):
    def __init__(self, foo: str):
        self._foo = foo
        if foo == "A":
            self._runA()
        else:
            self._runB()
        print(self._print)

    def _runA(self):
        print("Running _TestA")
        super().__init__(self._foo)

    def _runB(self):
        print("Running _TestB")
        super(_TestA, self).__init__(self._foo)


ta = TestAB("A") 
tb = TestAB("B")

这给予了
Running _TestA
testA-A
Running _TestB
testB-B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接