Просто, чтобы продолжить правильный ответ от @ chris-b, ниже приведены некоторые примеры, основанные на сценарии использования OP и шаблоне, который я уже пробовал (и в большинстве случаев потерпел неудачу, по крайней мере, с точки зрения красоты кода).
recap
Таким образом, если вы наберете super.__init__
в каждом написанном вами init классе, python будет хорошо следовать MRO, чтобы вызвать все inits для всехклассы, когда используется множественное наследование.super.__init__
работает, вызывая parent.__init__
и делегируя parent.__init__
для вызова всех его родственных элементов.
Следовательно, для простого class C(A, B)
, B.__init__
будет вызываться, только если A.__init__
сам вызывает super.__init__
, даже если C.__init__
использует super.__init__
.
Альтернатива состоит в том, чтобы вручную вызвать нужные вам единицы, например.A.__init__(self)
и B.__init__(self)
в C.__init__
;недостатком является то, что этот шаблон потенциально нарушает будущие унаследованные классы, которые вызывают super, и ожидают, что будут также вызваны все родительские блоки.Нужно / знать / что делают различные родительские элементы.
Таким образом, можно подумать, что использование super.__init__
все время - правильная вещь;но, как утверждает OP, эта «волшебная» цепочка вызовов обрывается, когда разные аргументы ожидаются разными init (обычное дело с шаблоном смешивания!).
Более подробную информацию можно найти в Как Python super () работает с множественным наследованием?
Есть ли идеальное решение?
К сожалению.похоже, что в Python использование множественного наследования (и миксиновых паттернов) требует определенных знаний о том, что происходит на нескольких уровнях;
даже пытаясь планировать расширенные случаи, принимая *args
и **kwargs
и вызывая super.__init__
, передавая все аргументы, не удастся, потому что объект. init () принимает только один параметр (self)!
Это показано в первом примере далее.
Чрезвычайно ужасный хак, которым я пользуюсь и который работает (хотя, возможно, не для всех возможных ситуаций), это заключать вызовы вsuper. init в попытке, кроме таких блоков, как:
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)