Python: 调用多个初始化函数的多重继承

3

我有两个类:A(需要一个参数进行初始化)和B(需要两个参数进行初始化),还有一个从A和B派生的第三个类C。

class A:
    def __init__(self, sval): 
        print("A: rcd value: ", sval)
        self.aval = sval

class B: 
    def __init__(self, sval, tval):
        print("B: rcd 2 values: ", sval, tval)
        self.aval=sval
        self.bval=tval

class C(A,B):
    def __init__(self, a, b, c):
        super().__init__(a) 
        super().__init__(b,c) # error here

c = C(1,2,3)

当我运行以上代码时,最后一行出现错误;调用的是A类的__init__方法,而不是B类的。
A: rcd value:  1
Traceback (most recent call last):
  File "inheritance.py", line 20, in <module>
    c = C(1,2,3)
  File "inheritance.py", line 16, in __init__
    super().__init__(b,c) 
TypeError: __init__() takes 2 positional arguments but 3 were given

如何在C类的__init__函数中调用A和B类的__init__函数?

注:我正在Debian Linux上使用Python 3.5.3,但我希望解决方案适用于Python2和Python3。


我已添加了Python-2.xPython-3.x标签,因为您没有指定使用哪个版本的Python,而且该解决方案适用于两个版本。 - AK47
你需要解决一个冲突:AB都有名为aval的属性,这两个属性中必须要重命名其中一个。如果你无法控制AB,那么你需要为其中一个定义适配器来进行解决。 - chepner
正确使用 super 的方法是让 AB 也使用它,然后在 C.__init__ 中只使用一次 super - chepner
2个回答

2

调用 A.__init__()B.__init__() 代替 super().__init__()

注:A和B是类名

class C(A,B):
    def __init__(self, a, b, c):
        A.__init__(self, a) 
        B.__init__(self, b, c)

Python 3 (https://repl.it/repls/CooperativeTransparentRobot):

原始回答:

class A:
    def __init__(self, sval): 
        print("A: rcd value: ", sval)
        self.aval = sval

class B: 
    def __init__(self, sval, tval):
        print("B: rcd 2 values: ", sval, tval)
        self.aval=sval
        self.bval=tval

class C(A,B):
    def __init__(self, a, b, c):
        A.__init__(self, a) 
        B.__init__(self, b,c) # this does not give error

c = C(1,2,3)

>> Python 3.6.1 (default, Dec 2015, 13:05:11)
>> [GCC 4.8.2] on linux
>> A: rcd value:  1
>> B: rcd 2 values:  2 3

在Python 2中也适用: https://repl.it/repls/GreenAbleBlockchain

最初的回答。


我可以保留第一个super调用吗:super().__init__(a),只使用B.__init__(self, b, c) - rnso
是的,但不要将 self 传递给 super().__init__() - AK47
我修改了之前出错的那一行的注释。这种方法运作良好,但是@chepner的回答似乎更加全面。 - rnso
请注意,当您调用 B.__init__ 时,它会覆盖 A.__init__ 设置的 aval。这并不是使用 A.__init__B.__init__ 明确的问题,而是 C 继承两个冲突类的问题。 - chepner

2

假设您可以控制AB并且可以避免在两者中使用相同的属性名称。然后为了正确使用super,请按以下方式定义它们。

原始答案:最初的回答

class A:
    def __init__(self, aval, **kwargs): 
        print("A: rcd value: ", aval)
        self.aval = sval
        super().__init__(**kwargs)

class B: 
    def __init__(self, b1val, b2val, **kwargs):
        print("B: rcd 2 values: ", b1val, b2val)
        self.b1val = b1val
        self.b2val = b2val
        super().__init__(**kwargs)

那么C.__init__只需要调用一次super

最初的回答:

class C(A, B):
    def __init__(self, aval, b1val, b2val, **kwargs):
        super().__init__(aval=aval, b1val=b1val, b2val=b2val, **kwargs)

c = C(1, 2, 3)

AK47 的解决方案有什么缺点吗? - rnso
至少就目前而言,它无法处理在AB中定义的冲突的aval属性;B.__init__只是覆盖了由A.__init__设置的值。此外(再次扩展原始问题),如果AB共享一个公共基类,则其__init__方法(假设AB都正确调用了它)将被调用两次,这可能会导致问题,具体取决于其定义。 - chepner
1
最终,这两种方法都需要对所涉及的类的定义进行一些注意。我不会说明确调用父类方法而不是使用 super 是绝对错误的;你只需要意识到每种方法的优缺点,并使用最适合你的类层次结构的方法即可。 - chepner
很好的解释。我觉得你可以把这些注释放在答案里,这样其他用户更可能会读到。 - rnso

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