Python вызывает базовый класс, используя super vs static

Куда бы я ни посмотрел, все продолжают говорить, какой классный super(). Но я склоняюсь к тому, чтобы не использовать super(), потому что это делает все намного сложнее, чем хотелось бы. Я видел несколько популярных примеров использования super(), но они никогда не показывают передачу позиционных обязательных аргументов в конструкторы базового класса.

Я знаю о проблеме с алмазами в Python, и super() предотвращает двойной вызов класса base_base. (в данном случае класс A)

Так ли уж плохо, что конструктор класса А вызывается дважды? (т. е. вариант 2)

Ниже приведен мой код для двух случаев.

Случай 1: Использование super()._ init _()

#abstractclass
class A(object):

    #abstractmethod
    def __init__(self, for_a, *args):
        self.val_a = for_a

#abstractclass
class B(A):

    #abstractmethod
    def __init__(self, for_a, for_b, for_c):
        super().__init__(for_a, for_c)
        self.val_b = for_b

#abstractclass
class C(A):

    #abstractmethod
    def __init__(self, for_a, for_c):
        super().__init__(for_a)
        self.val_c = for_c

class D(B, C):

    def __init__(self, for_a, for_b, for_c, for_d):
        super().__init__(for_a, for_b, for_c)
        self.val_d = for_d

class E(B):

    def __init__(self, for_a, for_b, for_e, *args):
        super().__init__(for_a, for_b, *args)
        self.val_e = for_e

newobject1 = D(1, 2, 3, 4)

newobject2 = E(10, 11, 12, 0)

Вариант 2: статический — с использованием base._ init _(self)

#abstractclass
class A(object):

    #abstractmethod
    def __init__(self, for_a):
        self.val_a = for_a

#abstractclass
class B(A):

    #abstractmethod
    def __init__(self, for_a, for_b):
        A.__init__(self, for_a)
        self.val_b = for_b

#abstractclass
class C(A):

    #abstractmethod
    def __init__(self, for_a, for_c):
        A.__init__(self, for_a)
        self.val_c = for_c

class D(B, C):

    def __init__(self, for_a, for_b, for_c, for_d):
        B.__init__(self, for_a, for_b)
        C.__init__(self, for_a, for_c)
        self.val_d = for_d

class E(B):

    def __init__(self, for_a, for_b, for_e):
        super().__init__(for_a, for_b)
        self.val_e = for_e

newobject1 = D(1, 2, 3, 4)

newobject2 = E(10, 11, 12)

person btgorman    schedule 08.03.2016    source источник
comment
Однако super не является просто заменой статического вызова родительского базового класса. Я бы рекомендовал прочитать как Супер считается супер, так и Python Super хорош, но вы не можете использовать его, чтобы понять, как правильно его использовать .   -  person chepner    schedule 09.03.2016


Ответы (2)


Так ли уж плохо, что конструктор класса А вызывается дважды?

Безусловно, если конструктор работает медленно, или если он не идемпотент, или если он получает ресурсы, такие как дескрипторы файлов или соединения с базой данных. В более сложной структуре наследования вы также можете вызвать конструкторы 4 или 6 раз или столько раз, сколько существует путей вверх по графу наследования от дочернего элемента к предку.

Я склоняюсь к тому, чтобы не использовать super(), потому что это делает все намного сложнее, чем хотелось бы.

Это не super что-то усложняет - это множественное наследование. Избегание super при множественном наследовании просто дает вам другие, возможно, более серьезные проблемы, чем проблемы super.

Я видел несколько популярных примеров использования super(), но они никогда не показывают передачу позиционных обязательных аргументов в конструкторы базового класса.

Это связано с тем, что аргументы позиционного конструктора плохо работают с множественным наследованием. Если вы хотите использовать подобную структуру множественного наследования, попробуйте сделать аргументы только ключевыми словами и использовать **kwargs для передачи нераспознанных аргументов для обработки другими классами. "Python super() считается супер" содержит несколько полезных советов как заставить его работать.

person user2357112 supports Monica    schedule 08.03.2016
comment
Спасибо за ответ! Я изучил, как использовать **kwargs, и я думаю, что они могут идеально подойти для моего варианта использования. - person btgorman; 09.03.2016

Идея состоит в том, что super() поддерживает порядок разрешения методов, чтобы вы могли написать ваши программы используют кооперативное наследование.

Совместное наследование имеет смысл, если вы хотите вызвать метод вашего суперкласса, но вы не знаете точно, какой это будет суперкласс — у вас есть только предположение, что метод имеет ту же сигнатуру, что и метод, который вы пишете.

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


Не будем забывать: множественное наследование — это рецепт беспорядка. Существуют соглашения (наборы ограничений), которых вы можете придерживаться, чтобы ваш код был понятным и чтобы спагетти оставались под контролем. Одна из идей заключается в использовании некоторых базовых классов в качестве примесей. Другая идея — кооперативное наследование. В Python есть такие языковые конструкции, как super, для их поддержки, но если вы можете выйти за эти границы. Это связано со своим набором проблем.

person Kos    schedule 08.03.2016