Python super() против метода Base.__init__
При определении подкласса существуют разные способы вызова метода __init__
родительского класса. Давайте начнем с базового класса и рассмотрим каждый из этих методов.
Для этого блога лучше открыть файл Python sample.py
и следовать инструкциям.
class Base(object):
def __init__ (self):
print "Base created"
Метод 1: Непосредственное использование родительской ссылки
class ChildA(Base):
def __init__ (self):
Base. __init__ (self)
print ("Child A initlaized")
Метод 2: Использование Super с дочерним классом
class ChildB(Base):
def __init__ (self):
print ("Child B initlaized")
super(ChildB, self). __init__ ()
Метод 3: Использование супер-метода.
class ChildC(Base):
def __init__ (self):
super(). __init__ ()
print ("Child C initlaized")
Вопросы
- Каковы плюсы и минусы каждого метода?
- Есть ли один-единственный правильный способ сделать это?
Когда вы запустите этот код как один скрипт Python, инициализируя дочерние классы A, B и C, вы не заметите абсолютно никакой разницы.
cA = ChildA()
cB = ChildB()
cC = ChildC()
Как мы можем демистифицировать это? Начнем с документации.
- Начиная с Python3,
super()
аналогиченsuper(ChildB, self).__init__()
. Это исключает один из трех методов.
- Чтобы сравнить
Base.__init__(self)
иsuper().__init__()
, нам нужно множественное наследование. Рассмотрим следующий фрагмент:
class Base1:
def __init__ (self):
print ("Base 1 created")
super(). __init__ ()
class Base2:
def __init__ (self):
print ("Base 2created")
super(). __init__ ()
Давайте напишем подклассы
class A1(Base1, Base2):
def __init__ (self):
print ("Child A1 Initialized")
super(). __init__ ()
class A2(Base2, Base1):
def __init__ (self):
print ("Child A Initialized")
super(). __init__ ()
Давайте инициализируем объекты
a1 = A1()
print ("\n\n")
a2 = A2()
Запустив приведенный выше фрагмент, мы получим следующий вывод:
Base 1 created
Base 2 created
Child A1 initialized
Base 2 created
Base 1 created
Child A2 initialized
В случае класса A1(Base1, Base2)
первым инициализируется Base1
, а затем Base2
. Это противоположность классу А2
. Можно сделать вывод, что методы вызываются в зависимости от порядка спецификации.
- Когда вы используете метод
Base1.__init__()
, вы теряете эту возможность Python.
- Когда вы введете новые иерархии, переименование классов станет кошмаром.
Итак, как Python узнает, какую функцию вызывать первой, вводя MRO
(порядок разрешения метода)
Порядок разрешения метода
Порядок разрешения метода (MRO
) обозначает способ, которым язык программирования разрешает метод или атрибут.
В случае одиночного наследования атрибут ищется только на одном уровне, при множественном наследовании интерпретатор Python ищет атрибут сам по себе, а затем его родительские элементы в порядке наследования. В случае A1 -> Base 1 -> Base 2
.
Можно использовать функцию mro
, чтобы найти порядок разрешения методов любого конкретного класса.
print (A2.mro())
** Output**
[<class ' __main__.A2'>, <class ' __main__.Base2'>, <class ' __main__.Base1'>, <class 'object'>]
Последний удар
Закомментируйте супер-вызовы в базовом классе и проверьте результат вашего скрипта.
class Base1:
def __init__ (self):
print ("Base 1 created")
# super(). __init__ ()
class Base2:
def __init__ (self):
print ("Base 2 created")
# super(). __init__ ()
Что ты видишь?
Base 1 created
Child A1 initlaized
Base 2 created
Child A2 initlaized
[<class ' __main__.A2'>, <class ' __main__.Base2'>, <class ' __main__.Base1'>, <class 'object'>]
Несмотря на наличие Base1
и Base2
в списке MRO
, mro не выполнит порядок, если функция super()
не будет распространена до базового класса, т. е. Python распространяет поиск атрибута только до тех пор, пока не найдет его. . Прокомментируйте метод init
в Base1
и убедитесь сами.
class Base1:
pass
# def __init__ (self):
# self.prop1 = "Base 1"
# self.prop11 = "Base 11"
# print ("Base 1 created")
# # super(). __init__ ()
На выход
Поскольку Python не может найти метод __init__
в Base1
, он проверяет Base2
, прежде чем отправить его в класс объекта.
Base 2 created
Child A1 initlaized
Base 2 created
Child A2 initlaized
[<class ' __main__.A2'>, <class ' __main__.Base2'>, <class ' __main__.Base1'>, <class 'object'>]
Люди спрашивают меня, почему я так люблю Python. Это не потому, что Python прост и удобен. Все эти вещи Python делает для того, чтобы облегчить нам задачу, а иногда и немного усложнить :)