python 多重继承使用 super 将参数传递给构造函数
- 2025-03-19 08:57:00
- admin 原创
- 52
问题描述:
考虑以下 Python 代码片段
class A(object):
def __init__(self, a):
self.a = a
class B(A):
def __init__(self, a, b):
super(B, self).__init__(a)
self.b = b
class C(A):
def __init__(self, a, c):
super(C, self).__init__(a)
self.c = c
class D(B, C):
def __init__(self, a, b, c, d):
#super(D,self).__init__(a, b, c) ???
self.d = d
我想知道如何将 和 传递a
给b
相应c
的基类的构造函数。
解决方案 1:
好吧,一般来说,在处理多重继承时,你的基类(不幸的是)应该为多重继承而设计。你示例中的类B
和C
不是,因此你找不到合适的方法来super
应用D
。
为多重继承设计基类的常见方法之一是让中级基类在其__init__
方法中接受它们不打算使用的额外参数,并将它们传递给它们的super
调用。
以下是使用 Python 实现此操作的一种方法:
class A(object):
def __init__(self,a):
self.a=a
class B(A):
def __init__(self,b,**kw):
self.b=b
super(B,self).__init__(**kw)
class C(A):
def __init__(self,c,**kw):
self.c=c
super(C,self).__init__(**kw)
class D(B,C):
def __init__(self,a,b,c,d):
super(D,self).__init__(a=a,b=b,c=c)
self.d=d
这看上去令人失望,但事实就是如此。
解决方案 2:
super()
不幸的是,如果不更改基类,则无法实现此功能。对B
或 的构造函数的任何调用C
都将尝试调用方法解析顺序中的下一个类,该类始终是B
或 ,C
而不是和类构造函数假定的A
类。B
`C`
另一种方法是明确调用构造函数,而不在每个类中使用super()
。
class A(object):
def __init__(self, a):
object.__init__()
self.a = a
class B(A):
def __init__(self, a, b):
A.__init__(self, a)
self.b = b
class C(A):
def __init__(self, a, c):
A.__init__(self, a)
self.c = c
class D(B, C):
def __init__(self, a, b, c, d):
B.__init__(self, a, b)
C.__init__(self, a, c)
self.d = d
这里仍存在一个缺点,因为A
构造函数将被调用两次,这在本例中实际上没有太大影响,但在更复杂的构造函数中可能会导致问题。您可以添加一个检查以防止构造函数运行多次。
class A(object):
def __init__(self, a):
if hasattr(self, 'a'):
return
# Normal constructor.
有些人会称这是 的缺点super()
,从某种意义上来说确实如此,但总的来说,这也是多重继承的缺点。菱形继承模式经常容易出错。而针对它们的许多解决方法会导致更加混乱和容易出错的代码。有时,最好的答案是尝试重构代码以减少使用多重继承。
解决方案 3:
一个关键概念:super
不引用父类。它引用 mro 列表中的下一个类,这取决于正在实例化的实际类。
所以在调用时super().__init__
,实际调用的方法是由调用框架决定的。
这就是为什么类必须专门为 mixin 设计的原因。
即使一个类只继承自object
,也应该调用super().__init__
。当然,当object__init__(**kwargs)
被调用时,kwargs
应该为空;否则会引发错误。
例子:
class AMix:
def __init__(self, a, **kwargs):
super().__init__(**kwargs)
self.a = a
class BMix:
def __init__(self, b, **kwargs):
super().__init__(**kwargs)
self.b = b
class AB(AMix, BMix):
def __init__(self, a, b):
super().__init__(a=a, b=b)
ab = AB('a1', 'b2')
print(ab.a, ab.b) # -> a1 b2
解决方案 4:
我对这里的答案并不完全满意,因为有时使用不同的参数为每个基类分别调用 super() 而不重构它们会非常方便。因此,我创建了一个名为 multinherit 的包,您可以使用该包轻松解决这个问题。https ://github.com/DovaX/multinherit
from multinherit.multinherit import multi_super
class A(object):
def __init__(self, a):
self.a = a
print(self.a)
class B(A):
def __init__(self, a, b):
multi_super(A,self,a=a)
self.b = b
print(self.b)
class C(A):
def __init__(self, a, c):
multi_super(A,self,a=a)
self.c = c
print(self.c)
class D(B, C):
def __init__(self, a, b, c, d):
multi_super(B,self,a=a,b=b)
multi_super(C,self,a=a,c=c)
self.d = d
print(self.d)
print()
print("d3")
d3=D(1,2,3,4)
print(d3._classes_initialized)
>>> d3
>>> 1
>>> 2
>>> 3
>>> 4
>>> [<class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>]
扫码咨询,免费领取项目管理大礼包!