只是为了扩展@chris-b 的正确答案,这里遵循一些基于 OP 用例和我已经尝试过的模式的示例(并且大部分都失败了,至少在代码美感方面)。
回顾
总而言之,如果您在编写的每个类init 中调用super.__init__,python 将很好地遵循 MRO 来调用所有类的所有 init,当使用多重继承时。
super.__init__ 工作调用parent.__init__,并委托parent.__init__ 调用它的所有兄弟姐妹。
因此,对于一个简单的class C(A, B),只有在A.__init__ 本身调用super.__init__ 时才会调用B.__init__,即使C.__init__ 使用super.__init__。
另一种方法是手动调用所需的 inits,例如。 A.__init__(self) 和 B.__init__(self) 在 C.__init__;缺点是这种模式可能会破坏将来调用 super 的继承类,并期望所有父 inits 也被调用。必须 / 知道 / 各种父级 init 的作用。
因此人们会认为一直使用super.__init__ 是正确的做法;但正如 OP 所述,当不同的 init 期望不同的参数时,这种“神奇”的调用链会中断(mixin 模式的常见问题!)。
更多信息可以在How does Python's super() work with multiple inheritance?找到
有完美的解决方案吗?
不幸的是。似乎在 Python 中使用多重继承(和 mixin 模式)需要对多个级别发生的事情有一定的了解;
即使尝试通过接受*args 和**kwargs 并调用super.__init__ 传递所有参数来计划扩展案例也会失败,因为object.init() 只接受一个参数(self)!
这在后面的第一个示例中显示。
我一直在使用的一个非常丑陋的技巧(尽管可能不适用于所有可能的情况)是将对 super.init 的调用包装在 try 中,但以下块除外:
try:
super(ThisClass, self).__init__(*args, arg1=arg1, arg2=arg2, **kwargs)
except TypeError as e:
# let's hope this TypeError is due to the arguments for object...
super(ThisClass, self).__init__()
这似乎可行 - 但真的很难看。
我做了一个要点:https://gist.github.com/stefanocrosta/1d113a6a0c79f853c30a64afc4e8ba0a
但以防万一这些是示例:
完整示例 1
class BaseClass(object):
def __init__(self, base_arg, base_arg2=None, *args, **kwargs):
print "\tBaseClass: {}, {}".format(base_arg, base_arg2)
super(BaseClass, self).__init__(*args, base_arg=base_arg, base_arg2=base_arg2, **kwargs)
class MixinClass(object):
def __init__(self, mixin_arg, *args, **kwargs):
print "\tMixinClass: {}".format(mixin_arg)
super(MixinClass, self).__init__()
class MixinClassB(object):
def __init__(self, mixin_arg, *args, **kwargs):
print "\tMixinClassB: {}".format(mixin_arg)
super(MixinClassB, self).__init__(*args, mixin_arg=mixin_arg, **kwargs)
class ChildClassA(BaseClass, MixinClass):
"""
Let's make it work for this case
"""
def __init__(self, base_arg, mixin_arg, base_arg2=None):
print "Initializing {}: base_arg: {} mixin_arg: {} base_arg2: {}".format(
self.__class__.__name__, base_arg, mixin_arg, base_arg2)
super(ChildClassA, self).__init__(base_arg=base_arg, mixin_arg=mixin_arg, base_arg2=base_arg2)
class ChildClassB(BaseClass, MixinClass):
"""
Same as above, but without specifying the super.__init__ arguments names
"""
def __init__(self, base_arg, mixin_arg, base_arg2=None):
print "Initializing {}: base_arg: {} mixin_arg: {} base_arg2: {}".format(
self.__class__.__name__, base_arg, mixin_arg, base_arg2)
# If you don't specify the name of the arguments, you need to use the correct order of course:
super(ChildClassB, self).__init__(base_arg, base_arg2, mixin_arg)
class ChildClassC(BaseClass, MixinClassB, MixinClass):
"""
Now let's simply add another mixin: before...
"""
def __init__(self, base_arg, mixin_arg, base_arg2=None):
print "Initializing {}: base_arg: {} mixin_arg: {} base_arg2: {}".format(
self.__class__.__name__, base_arg, mixin_arg, base_arg2)
super(ChildClassC, self).__init__(base_arg=base_arg, mixin_arg=mixin_arg, base_arg2=base_arg2)
class ChildClassD(BaseClass, MixinClass, MixinClassB):
"""
Now let's simply add another mixin: ..and after
"""
def __init__(self, base_arg, mixin_arg, base_arg2=None):
print "Initializing {}: base_arg: {} mixin_arg: {} base_arg2: {}".format(
self.__class__.__name__, base_arg, mixin_arg, base_arg2)
super(ChildClassD, self).__init__(base_arg=base_arg, mixin_arg=mixin_arg, base_arg2=base_arg2)
childA = ChildClassA(1, 3, 2) # note the order of the arguments - the mixin arg is interleaved
childB = ChildClassB(1, 3, 2)
childC = ChildClassC(1, 3, 2)
childD = ChildClassD(1, 3, 2)
完整示例 2:
class BaseClass(object):
def __init__(self, base_arg, base_arg2=None, *args, **kwargs):
print "\tBaseClass: {}, {}".format(base_arg, base_arg2)
try:
super(BaseClass, self).__init__(*args, base_arg=base_arg, base_arg2=base_arg2, **kwargs)
except:
super(BaseClass, self).__init__()
class MixinClass(object):
def __init__(self, mixin_arg, *args, **kwargs):
print "\tMixinClass: {}".format(mixin_arg)
try:
super(MixinClass, self).__init__(*args, mixin_arg=mixin_arg, **kwargs)
except:
super(MixinClass, self).__init__()
class MixinClassB(object):
def __init__(self, mixin_arg, *args, **kwargs):
print "\tMixinClassB: {}".format(mixin_arg)
try:
super(MixinClassB, self).__init__(*args, mixin_arg=mixin_arg, **kwargs)
except:
super(MixinClassB, self).__init__()
class ChildClassA(BaseClass, MixinClass):
"""
Let's make it work for this case
"""
def __init__(self, base_arg, mixin_arg, base_arg2=None):
print "Initializing {}: base_arg: {} mixin_arg: {} base_arg2: {}".format(
self.__class__.__name__, base_arg, mixin_arg, base_arg2)
super(ChildClassA, self).__init__(base_arg=base_arg, mixin_arg=mixin_arg, base_arg2=base_arg2)
class ChildClassC(BaseClass, MixinClassB, MixinClass):
"""
Now let's simply add another mixin: before...
"""
def __init__(self, base_arg, mixin_arg, base_arg2=None):
print "Initializing {}: base_arg: {} mixin_arg: {} base_arg2: {}".format(
self.__class__.__name__, base_arg, mixin_arg, base_arg2)
super(ChildClassC, self).__init__(base_arg=base_arg, mixin_arg=mixin_arg, base_arg2=base_arg2)
class ChildClassD(BaseClass, MixinClass, MixinClassB):
"""
Now let's simply add another mixin: ..and after
"""
def __init__(self, base_arg, mixin_arg, base_arg2=None):
print "Initializing {}: base_arg: {} mixin_arg: {} base_arg2: {}".format(
self.__class__.__name__, base_arg, mixin_arg, base_arg2)
super(ChildClassD, self).__init__(base_arg=base_arg, mixin_arg=mixin_arg, base_arg2=base_arg2)
try:
base = BaseClass(1, 2)
except Exception as e:
print "Failed because object.__init__ does not expect any argument ({})".format(e)
childA = ChildClassA(1, 3, 2) # note the order of the arguments - the mixin arg is interleaved
childC = ChildClassC(1, 3, 2)
childD = ChildClassD(1, 3, 2)