Подтвердить что ты не робот

Разница между супер() и вызывающим суперклассом напрямую

В Python 2.7 и 3 я использую следующий метод для вызова функции суперкласса:

class C(B):
    def __init__(self):
        B.__init__(self)

Я также вижу, что можно заменить B.__init__(self) на super(B, self).__init__() и в python3 super().__init__().

Есть ли какие-либо преимущества или недостатки в этом? Имеет смысл называть его от B напрямую для меня, по крайней мере, но, возможно, есть веская причина, по которой super() можно использовать только при использовании метаклассов (чего я обычно избегаю).

4b9b3361

Ответ 1

Для одиночного наследования super() является просто более удобным способом ссылаться на базовый тип. Таким образом, вы делаете код более удобным для обслуживания, например, если вы хотите изменить имя базового типа. Когда вы используете super везде, вам просто нужно изменить его в строке class.

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

Это по существу позволяет типам иметь свойство алмаза, например. у вас есть единственный базовый тип A и два типа B и C, которые оба происходят из A. И тогда у вас есть тип E, который наследуется как от B, так и от C (что делает его неявным наследованием от A слишком-дважды). Если вы вызовете методы базовых типов прямо сейчас, вы в конечном итоге вызовете метод "Как" дважды. Но используя super, он вызывается только один раз:

class A (object):
    def __init__ (self):
        super().__init__()
        print('A')

class B (A):
    def __init__ (self):
        super().__init__()
        print('B')

class C (A):
    def __init__ (self):
        super().__init__()
        print('C')

class D (C, B):
    def __init__ (self):
        super().__init__()
        print('D')

Когда мы теперь создаем экземпляр D, получаем следующий вывод:

>>> D()
A
B
C
D
<__main__.D object at 0x000000000371DD30>

Теперь давайте снова все это сделаем с помощью ручного вызова метода базовых типов:

class A2 (object):
    def __init__ (self):
        print('A2')

class B2 (A2):
    def __init__ (self):
        A2.__init__(self)
        print('B2')

class C2 (A2):
    def __init__ (self):
        A2.__init__(self)
        print('C2')

class D2 (C2, B2):
    def __init__ (self):
        B2.__init__(self)
        C2.__init__(self)
        print('D2')

И это результат:

>>> D2()
A2
B2
A2
C2
D2
<__main__.D2 object at 0x0000000003734E48>

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

Ответ 2

Не могли бы вы объяснить, почему мы печатаем super().__init__() внутри основного класса A? Похоже, что он отлично работает без этой строки.