Вызов конструкторов суперкласса в python с разными аргументами

class A():
    def __init__( self, x, y):
        self.x = x
        self.y = y

class B():
    def __init__( self, z=0):
        self.z = z  

class AB(A,B):
    def __init__( self, x, y, z=0):
        ?

Как я могу заставить конструктор AB вызвать конструкторы для A и B с соответствующими аргументами?

Я пробовал

class AB(A,B):
    def __init__( self, x, y, z=0):
        A.__init__(x,y)
        B.__init__(z)

но это дает мне ошибку.

Ответ 1

Вы не прошли self.

class AB(A, B):
    def __init__(self, x, y, z=0):
        A.__init__(self, x, y)
        B.__init__(self, z)

Обратите внимание, что если эта иерархия наследования становится более сложной, вы столкнетесь с проблемами с конструкторами, которые не выполняют или не могут быть повторно обработаны. Посмотрите super (и проблемы с помощью super), и не забывайте наследовать от object, если вы на 2.x, и ваш класс не наследует ничего.

Ответ 2

Другие ответы предложили добавить self к первому параметру.

Но обычно вызовы __init__ в родительских классах выполняются super.

Рассмотрим следующий пример:

class A(object):
    def __init__(self, x):
        print('__init__ is called in A')
        self.x = x


class B(object):
    def __init__(self, *args, **kwargs):
        print('__init__ is called in B')
        super(B, self).__init__(*args, **kwargs)


class AB(B, A):
    def __init__(self, *args, **kwargs):
        print('__init__ is called in AB')
        super(AB, self).__init__(*args, **kwargs)

AB класс содержит порядок, в котором должны быть вызваны конструкторы и инициализаторы:

>>> AB.__mro__
(<class '__main__.AB'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)

Смотрите, что вызывается первый AB __init__, затем B 's, затем A' s, а затем object.

Проверить:

>>> ab = AB(1)
__init__ is called in AB
__init__ is called in B
__init__ is called in A

Но эти вызовы через эту цепочку выполняются super. Когда мы вводим super(AB, self), это означает: найдите следующий класс после AB в __mro__ цепочке self.

Затем мы должны вызывать super в B, ища следующий класс в цепочке после B: super(B, self).

Важно использовать super и не вызывать вручную A.__init__(self,...) и т.д., так как это может привести к проблемам позже. Прочтите это для дополнительной информации.

Итак, если вы придерживаетесь super, тогда возникает проблема. __init__ методы в ваших классах ожидают разные параметры. И вы не можете точно знать порядок, в котором super будет вызывать методы в этих классах. Порядок определяется алгоритмом C3 во время создания класса. В подклассах другие классы могут проходить между цепочкой вызовов. Таким образом, вы не можете иметь разные параметры в __init__, так как в этом случае вам всегда нужно будет рассмотреть всю цепочку наследования, чтобы понять, как будут вызываться методы __init__.

Например, рассмотрите возможность добавления классов C(A) и D(B) и подкласса CD. Тогда A больше не будет вызываться после B, но после C.

class A(object):
    def __init__(self, *args, **kwargs):
        print('__init__ is called in A')
        super(A, self).__init__(*args, **kwargs)


class B(object):
    def __init__(self, *args, **kwargs):
        print('__init__ is called in B')
        super(B, self).__init__(*args, **kwargs)

class AB(B,A):
    def __init__(self, *args, **kwargs):
        print('__init__ is called in AB')
        super(AB, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, *args, **kwargs):
        print('__init__ is called in C')
        super(C, self).__init__(*args, **kwargs)

class D(B):
    def __init__(self, *args, **kwargs):
        print('__init__ is called in D')
        super(D, self).__init__(*args, **kwargs)


class CD(D,C):
    def __init__(self, *args, **kwargs):
        print('__init__ is called in CD')
        super(CD, self).__init__(*args, **kwargs)

class ABCD(CD,AB):
    def __init__(self, *args, **kwargs):
        print('__init__ is called in ABCD')
        super(ABCD, self).__init__(*args, **kwargs)




>>> abcd = ABCD()
__init__ is called in ABCD
__init__ is called in CD
__init__ is called in D
__init__ is called in AB
__init__ is called in B
__init__ is called in C
__init__ is called in A

Поэтому я думаю, что неплохо подумать об использовании delegation вместо наследования здесь.

class AB(object):
    def __init__(self, x, y, z=0):
        self.a = A(x,y)
        self.b = B(z)

Итак, вы просто создаете экземпляры A и B классов A и B внутри объекта AB. А затем они могут использовать их, как вам нужно, с помощью методов, ссылаясь на self.a и self.b.

Использование или отсутствие делегирования зависит от вашего дела, которое не ясно из вашего вопроса. Но это может быть вариант для рассмотрения.