2014-11-14 5 views
20

У меня есть три класса: A, B и C.Множественное наследование в python3 с различными сигнатурами

C наследует от A и B (в указанном порядке). Подписи конструктора A и B различны. Как я могу вызвать методы __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() 

дает ошибку:

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 

я нашел this resource, который объясняет Mutiple удела с разным набором аргументов, но они предлагают использовать *args и **kwargs использовать для всех аргументов. Я считаю это очень уродливым, так как я не вижу из вызова конструктора в дочернем классе, какие параметры я передаю родительским классам.

+1

Вы используете 'super' неправильно, это должно быть просто' супер (C, самость) .__ init__ 'в' C .__ init__'. Поскольку у вас разные подписи, у вас нет выбора, кроме как обрабатывать произвольные '* args, ** kwargs', если вы хотите использовать' super'. – jonrsharpe

+0

'super()' - это когда вы используете mixins. Не используйте его, когда у вас разные подписи. –

ответ

38

Do неsuper(baseclass, ...), если вы не знаете, что делаете. Первый аргумент super() сообщает ему, что класс skip при поиске следующего метода использования. Например. 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) 

Использование кооперативной 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) 
+4

Иисус Христос, поведение супер (A, self), пропуская A '__init__', очень противоречит интуиции ... Как бы то ни было, ваш ответ просто ОТЛИЧНО, большое вам спасибо! –

+1

@NikolaiTschacher: как бы вы попросили метод в классе * next * в MRO, не зная, что это за класс? Однако вы всегда знаете ** текущий класс **, который вы определяете для метода. –

+0

Я думал, что super() без какого-либо класса в качестве аргумента просто перебирает MRO, как он был определен в заявлении класса подкласса: MRO для класса A (B, C, D) - A, B, C, D. Кроме того, я предположил, что вызов super (A, self) с явным аргументом просто посещает класс, который был предоставлен в супервызове, и что вызывающему нужно проверить правильность построения любых других родительских классов. Таким образом, супер (A, я) в моем мышлении только когда-либо посещал класс A, больше ничего. Я практически думал, что A .__ init __ (self) эквивалентен супер (A, self). Кроме того, я не полностью понял super() Я думаю ... –

Смежные вопросы