Вопрос-Ответ

How does Python's super() work with multiple inheritance?

Как функция super () в Python работает с множественным наследованием?

Как super() работает с множественным наследованием? Например, учитывая:

class First(object):
def __init__(self):
print "first"

class Second(object):
def __init__(self):
print "second"

class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"

К какому родительскому методу Third относится super().__init__? Могу ли я выбрать, какой из них выполняется?

Я знаю, что это как-то связано с порядком разрешения метода (MRO).

Переведено автоматически
Ответ 1

Это описано с достаточным количеством деталей самим Гвидо в его сообщении в блоге Порядок разрешения метода (включая две предыдущие попытки).

В вашем примере Third() вызовет First.__init__. Python ищет каждый атрибут в родительском классе, поскольку они перечислены слева направо. В данном случае мы ищем __init__. Итак, если вы определяете

class Third(First, Second):
...

Python начнет с поиска в First, и, если у First нет атрибута, то он будет искать в Second.

Эта ситуация усложняется, когда пути наследования начинают пересекаться (например, если First унаследовано от Second). Прочитайте ссылку выше для получения более подробной информации, но, в двух словах, Python попытается поддерживать порядок, в котором каждый класс отображается в списке наследования, начиная с самого дочернего класса.

Итак, например, если у вас было:

class First(object):
def __init__(self):
print "first"

class Second(First):
def __init__(self):
print "second"

class Third(First):
def __init__(self):
print "third"

class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print "that's it"

MRO будет [Fourth, Second, Third, First].

Кстати: если Python не может найти согласованный порядок разрешения метода, он вызовет исключение, вместо того чтобы вернуться к поведению, которое может удивить пользователя.

Пример неоднозначного MRO:

class First(object):
def __init__(self):
print "first"

class Second(First):
def __init__(self):
print "second"

class Third(First, Second):
def __init__(self):
print "third"

Должно Third быть MRO [First, Second] или [Second, First]? Очевидного ожидания нет, и Python выдаст ошибку:

TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First

Почему в приведенных выше примерах отсутствуют super() вызовы? Смысл примеров в том, чтобы показать, как строится MRO. Они не предназначены для печати "first\nsecond\third" или чего-то еще. Вы можете – и должны, конечно, - поиграть с примером, добавить super() вызовы, посмотреть, что получится, и получить более глубокое представление о модели наследования в Python. Но моя цель здесь - упростить и показать, как строится MRO. И он построен так, как я объяснил:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
<class '__main__.Second'>, <class '__main__.Third'>,
<class '__main__.First'>,
<type 'object'>)
Ответ 2

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

class First(object):
def __init__(self):
super(First, self).__init__()
print("first")

class Second(object):
def __init__(self):
super(Second, self).__init__()
print("second")

class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("third")

Вывод:

>>> Third()
second
first
third

super() Вызов находит следующий метод в MRO на каждом шаге, поэтому First и Second он тоже должен быть, иначе выполнение останавливается в конце Second.__init__().


Без super() вызовов в First и Second вывод отсутствует second:

>>> Third()
first
third
Ответ 3

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

Что вам нужно понимать, так это то, что super(MyClass, self).__init__() предоставляет метод next __init__ в соответствии с используемым алгоритмом упорядочения разрешения методов (MRO) в контексте полной иерархии наследования.

Эта последняя часть имеет решающее значение для понимания. Давайте еще раз рассмотрим пример:

#!/usr/bin/env python2

class First(object):
def __init__(self):
print "First(): entering"
super(First, self).__init__()
print "First(): exiting"

class Second(object):
def __init__(self):
print "Second(): entering"
super(Second, self).__init__()
print "Second(): exiting"

class Third(First, Second):
def __init__(self):
print "Third(): entering"
super(Third, self).__init__()
print "Third(): exiting"

Согласно этой статье о порядке разрешения метода, написанной Гвидо ван Россумом, порядок разрешения __init__ вычисляется (до Python 2.3) с использованием "обхода по глубине слева направо". :

Third --> First --> object --> Second --> object

После удаления всех дубликатов, за исключением последнего, мы получаем :

Third --> First --> Second --> object

Итак, давайте проследим, что происходит, когда мы создаем экземпляр Third класса, например, x = Third().


  1. Согласно MRO Third.__init__ выполняется.

    • С принтами Third(): entering

    • затем super(Third, self).__init__() выполняется и возвращается MRO, First.__init__ который вызывается.


  2. First.__init__ выполняется.

    • С принтами First(): entering

    • затем super(First, self).__init__() выполняется и возвращается MRO, Second.__init__ который вызывается.


  3. Second.__init__ выполняется.

    • С принтами Second(): entering

    • затем super(Second, self).__init__() выполняется и возвращается MRO, object.__init__ который вызывается.


  4. object.__init__ выполняется (в коде нет инструкций print)

  5. выполнение возвращается к Second.__init__, который затем выводит Second(): exiting

  6. выполнение возвращается к First.__init__, который затем выводит First(): exiting

  7. выполнение возвращается к Third.__init__, который затем выводит Third(): exiting

Здесь подробно объясняется, почему создание экземпляра Third() приводит к :

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting

Алгоритм MRO был улучшен начиная с Python 2.3, чтобы хорошо работать в сложных случаях, но я предполагаю, что использование "обхода слева направо в глубину" + "ожидаемое удаление дубликатов последним" все еще работает в большинстве случаев (пожалуйста, прокомментируйте, если это не так). Обязательно прочтите сообщение в блоге Guido!

Ответ 4

Это известно как проблема с бриллиантами, на странице есть запись на Python, но, короче говоря, Python будет вызывать методы суперкласса слева направо.

python