python3中具有不同签名的多重继承

Nik*_*her 43 python inheritance multiple-inheritance python-3.x

我有三个类:A,BC.

C继承自AB(按此顺序).构造函数的签名AB不同.如何调用__init__两个父类的方法?

我在代码中的努力:

class A(object):
    def __init__(self, a, b):
        super(A, self).__init__()
        print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))

class B(object):
    def __init__(self, q):
        super(B, self).__init__()
        print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))

class C(A, B):
    def __init__(self):
        super(A, self).__init__(1, 2)
        super(B, self).__init__(3)

c = C()
Run Code Online (Sandbox Code Playgroud)

产生错误:

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    c = C()
  File "test.py", line 13, in __init__
    super(A, self).__init__(1, 2)
TypeError: __init__() takes 2 positional arguments but 3 were given
Run Code Online (Sandbox Code Playgroud)

我发现这个资源用不同的参数集解释了多重继承,但他们建议使用*args**kwargs使用所有参数.我认为这非常难看,因为我无法从子类中的构造函数调用中看到我传递给父类的哪种参数.

Mar*_*ers 82

千万不能使用super(baseclass, ...),除非你知道自己在做什么.第一个参数super()告诉它在查找下一个要使用的方法时要跳过哪个类.例如,super(A, ...)将着眼于MRO,查找A,然后开始寻找__init__接下来的基类,不是 A本身.因为C,MRO是(C, A, B, object),所以super(A, self).__init__会找到B.__init__.

对于这些情况,你不希望使用合作继承,而是直接引用A.__init__B.__init__替代.super()只有在您调用的方法具有相同的签名或者使用*args和吞下不支持的参数时才应该使用**vargs.在这种情况下,只super(C, self).__init__()需要一个呼叫,MRO命令中的下一个类将负责处理呼叫.

换句话说:当你使用时super(),你无法知道MRO中接下来会是什么类,所以这个类更好地支持你传递给它的参数.如果不是这样,请不要使用super().

__init__直接调用基本方法:

class A(object):
    def __init__(self, a, b):
        print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))

class B(object):
    def __init__(self, q):
        print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))

class C(A, B):
    def __init__(self):
        # Unbound functions, so pass in self explicitly
        A.__init__(self, 1, 2)
        B.__init__(self, 3)
Run Code Online (Sandbox Code Playgroud)

使用合作super():

class A(object):
    def __init__(self, a=None, b=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))

class B(object):
    def __init__(self, q=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))

class C(A, B):
    def __init__(self):
        super().__init__(a=1, b=2, q=3)
Run Code Online (Sandbox Code Playgroud)

  • 耶稣基督,超级(A,自我)跳过A的'__init__`的行为非常违反直觉......无论如何,你的回答非常优秀,非常感谢你! (13认同)
  • @NikolaiTschacher:如果您了解`super()`试图解决的问题,它会有所帮助:在具有菱形模式继承(多个子类使用的基类)的复杂层次结构中按顺序调用*next*方法.在这种情况下使用显式的`BaseClass .__ init__`调用变得容易出错,最终会多次调用`SharedBaseClass .__ init__`.通过使用MRO,您可以获得有保证的订单,但该订单是基于继承的*动态*.这意味着你不应该关心下一堂课是什么,只要确保他们中的任何一门都可以接受这个电话. (3认同)