Супер init и родительский.__ init__

Мы следуем классу Python в книге, с которой я связан, и не используют super для инициализации из унаследованного класса. У меня возникают проблемы, дающие ясное, прямое описание различий между этими двумя случаями:

class Parent(object):

    def __init__(self):
         ....


class Child(Parent):

    def __init__(self):
        super(Child, self).__init__()


class Child(Parent):

    def __init__(self):
        Parent.__init__(self)

Но я смущен, признавшись, что не могу объяснить, почему один случай превосходит другого. Может ли кто-нибудь дать четкое объяснение?

Ответы

Ответ 1

Цель супер - обработать алмазы наследования. Если класс структура наследования использует только однонаследование, тогда использование функции super() будет приводят к тем же вызовам, что и явные вызовы в "родительский" класс.

Рассмотрим этот наследование алмаза:

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

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

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

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

foo = D()

который печатает

Running D.__init__
Running B.__init__
Running C.__init__
Running A.__init__

а если мы изменим B на B2 и будем использовать явные вызовы родительскому __init__:

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

class D2(B2,C):
    def __init__(self):
        print('Running D.__init__')
        super(D2,self).__init__()

bar = D2()

тогда цепочка вызовов init становится

Running D.__init__
Running B.__init__
Running A.__init__

Таким образом, вызов C.__init__ полностью пропущен.


Нет предпочтительного варианта.

Если вы можете гарантировать, что не хотите поддерживать множественное наследование, тогда явные родительские вызовы проще и понятнее.

Если вы хотите поддерживать множественное наследование сейчас или в будущем, вам нужно использовать super(). Но поймите, что есть некоторые ловушки, связанные с использованием супер, но с правильное использование этих ловушек можно избежать.

Ответ 2

Основная цель super(Child, self).__init__(self) - обеспечить правильную инициализацию в случае множественного наследования с структурами наследования алмазов. Если вы явно вызываете конструкторы базового класса с множественным наследованием, некоторые инициализаторы могут вызываться дважды. С единственным наследованием нет функциональной разницы между использованием супер и явным вызовом метода базового класса __init__(). Обратите внимание, что поскольку все объекты подкласса класса нового класса python, множественное наследование всегда включает в себя наследование алмазов.

super имеет меньшее преимущество сокращения, требует изменений, если вы переименуете или измените базовый класс.

В python 3 аргументы super являются необязательными, поэтому вы можете просто сделать super().__init__(self). Python 2 все еще требует, чтобы вы предоставили аргументы явно.