Я довольно новичок в объектно-ориентированном программировании на 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 () и почему?
А что, если вы хотите запустить другой? Я знаю, что это как-то связано с порядком разрешения методов Python ( MRO ).
python
multiple-inheritance
Callisto
источник
источник
super()
есть какое-либо применение. Я не рекомендовал бы использовать это с классами, использующими линейное наследование, где это просто бесполезные накладные расходы.super()
заключается в том, что он заставляет каждый подкласс использовать его, а когда он не используетсяsuper()
, каждый подкласс может сам решать. Если разработчик, использующий его, не знаетsuper()
или не знает, что он использовался, могут возникнуть проблемы с mro, которые очень трудно отследить.Ответы:
Это подробно описано с достаточным количеством подробностей самим Гвидо в его посте « Постановление о разрешении метода» (включая две более ранние попытки).
В вашем примере
Third()
позвонимFirst.__init__
. Python ищет каждый атрибут в родительских классах, поскольку они перечислены слева направо. В этом случае мы ищем__init__
. Итак, если вы определитеPython начнет с просмотра
First
, и, если уFirst
него нет атрибута, он будет смотретьSecond
.Эта ситуация становится более сложной, когда наследование начинает пересекать пути (например, если
First
наследуется отSecond
). Прочитайте ссылку выше для получения более подробной информации, но, в двух словах, Python будет пытаться поддерживать порядок, в котором каждый класс появляется в списке наследования, начиная с самого дочернего класса.Так, например, если у вас было:
MRO будет
[Fourth, Second, Third, First].
Кстати, если Python не может найти согласованный порядок разрешения методов, он вызовет исключение, вместо того чтобы вернуться к поведению, которое может удивить пользователя.
Отредактировано, чтобы добавить пример неоднозначного MRO:
Должно
Third
быть MRO[First, Second]
или[Second, First]
? Нет очевидных ожиданий, и Python выдаст ошибку:Редактировать: Я вижу, как несколько человек утверждают, что в приведенных выше примерах отсутствуют
super()
вызовы, поэтому позвольте мне объяснить: цель примеров - показать, как строится MRO. Они не предназначены для печати "первая \ третья \ третья" или что-то еще. Вы можете - и, конечно, должны поиграться с этим примером, добавитьsuper()
вызовы, посмотреть, что происходит, и глубже понять модель наследования Python. Но моя цель здесь состоит в том, чтобы сделать это простым и показать, как строится MRO. И это построено, как я объяснил:источник
Ваш код и другие ответы все глючат. Они пропускают
super()
вызовы в первых двух классах, которые требуются для совместной работы подклассов.Вот исправленная версия кода:
super()
Вызов находит следующий метод в MRO на каждый шаг, поэтому первые и вторые должны иметь это тоже, в противном случае выполнение останавливается в концеSecond.__init__()
.Вот что я получаю:
источник
super
он либо не будет работать (из-за несоответствия параметров), либо не вызовет несколько баз (потому что вы не написали ниsuper
в одной из баз, которая разрывает ссылку)!Я хотел немного развить ответ безжизненным, потому что когда я начал читать о том, как использовать super () в иерархии множественного наследования в Python, я не получил его сразу.
Что вам нужно понять, так это
super(MyClass, self).__init__()
предоставить следующий__init__
метод в соответствии с используемым алгоритмом упорядочения методов разрешения (MRO) в контексте полной иерархии наследования .Эта последняя часть имеет решающее значение для понимания. Давайте рассмотрим пример еще раз:
В соответствии с этой статьей о Порядке разрешения методов Гвидо ван Россумом, порядок разрешения
__init__
вычисляется (до Python 2.3) с использованием «обхода слева направо в глубину»:После удаления всех дубликатов, кроме последнего, получаем:
Итак, давайте проследим, что происходит, когда мы создаем экземпляр
Third
класса, напримерx = Third()
.Third.__init__
выполняет.Third(): entering
super(Third, self).__init__()
выполняется и MRO возвращает,First.__init__
что называется.First.__init__
выполняется.First(): entering
super(First, self).__init__()
выполняется и MRO возвращает,Second.__init__
что называется.Second.__init__
выполняется.Second(): entering
super(Second, self).__init__()
выполняется и MRO возвращает,object.__init__
что называется.object.__init__
выполняет (там нет операторов печати в коде)Second.__init__
что затем печатаетSecond(): exiting
First.__init__
что затем печатаетFirst(): exiting
Third.__init__
что затем печатаетThird(): exiting
Это подробно объясняет, почему создание экземпляра Third () приводит к:
Алгоритм MRO был улучшен с Python 2.3 и выше, чтобы хорошо работать в сложных случаях, но я предполагаю, что использование «обхода слева направо в глубину» + «удаление дубликатов, ожидающих последнего» все еще работает в большинстве случаев (пожалуйста, комментарий, если это не так). Обязательно прочитайте пост в блоге Гвидо!
источник
Third
не наследовать отSecond
, тоsuper(First, self).__init__
вызоветobject.__init__
и после возврата будет напечатано «first». Но посколькуThird
наследует от обоихFirst
иSecond
вместо вызоваobject.__init__
послеFirst.__init__
MRO диктует, чтоobject.__init__
сохраняется только последний вызов , а операторы print вFirst
иSecond
не достигаются доobject.__init__
возврата. ПосколькуSecond
был последний звонокobject.__init__
, он возвращается внутрь,Second
прежде чем вернуться внутрьFirst
.List[subclass]
как подклассList[superclass]
if ( происходит из модуля PEP 483). iirc).subclass
superclass
List
typing
Это известно как проблема с бриллиантами , на странице есть запись на Python, но вкратце Python будет вызывать методы суперкласса слева направо.
источник
object
четвертыйЭто то, как я решил проблему множественного наследования с разными переменными для инициализации и наличия нескольких MixIns с одним и тем же вызовом функции. Я должен был явно добавить переменные к переданным ** kwargs и добавить интерфейс MixIn, чтобы быть конечной точкой для супер вызовов.
Вот
A
это расширяемый базовый класс иB
иC
классы подмешать оба , которые обеспечивают функциюf
.A
иB
оба ожидают параметраv
по своему__init__
иC
ожидаютw
. Функцияf
принимает один параметрy
.Q
наследует от всех трех классов.MixInF
это миксин интерфейс дляB
иC
.источник
args
/,kwargs
а не именованных параметров.Я понимаю, что это не дает прямого ответа на
super()
вопрос, но я чувствую, что это достаточно важно, чтобы поделиться.Существует также способ прямого вызова каждого унаследованного класса:
Просто обратите внимание , что если вы делаете это таким образом, вы должны будете звонить каждый вручную , так как я уверен , что
First
«s__init__()
не будет называться.источник
First
иSecond
оба наследуют другой класс и вызывают его напрямую, то этот общий класс (начальная точка ромба) вызывается дважды. Супер избегает этого.object
что тебя дважды называют. Я не думал об этом. Я просто хотел подчеркнуть, что вы вызываете родительские классы напрямую.В целом
Предполагая, что все происходит от
object
(вы сами, если нет), Python вычисляет порядок разрешения методов (MRO) на основе вашего дерева наследования классов. MRO удовлетворяет 3 свойствам:Если такого порядка нет, ошибки Python. Внутренняя работа этого - линеаризация C3 родословной классов. Прочтите все об этом здесь: https://www.python.org/download/releases/2.3/mro/
Таким образом, в обоих приведенных ниже примерах это:
Когда вызывается метод, первое вхождение этого метода в MRO - это тот, который вызывается. Любой класс, который не реализует этот метод, пропускается. Любой вызов
super
внутри этого метода вызовет следующее вхождение этого метода в MRO. Следовательно, важно как порядок, в котором вы размещаете классы в наследовании, так и то, куда вы помещаете вызовыsuper
в методах.С
super
первым в каждом методеChild()
Выходы:С
super
последним в каждом методеChild()
Выходы:источник
Left
с помощьюsuper()
изChild
. Предположим, я хочу получить доступRight
изнутриChild
. Есть ли способ получить доступRight
сChild
помощью супер? Или я должен прямо позвонитьRight
изнутриsuper
?О @ calfzhou свой комментарий , вы можете использовать, как обычно,
**kwargs
:Пример работающего онлайн
Результат:
Вы также можете использовать их позиционно:
но вы должны помнить MRO, это действительно сбивает с толку.
Я могу быть немного раздражающим, но я заметил, что люди забывают каждый раз использовать
*args
и**kwargs
когда они переопределяют метод, в то время как это одно из немногих действительно полезных и разумных способов использования этих «магических переменных».источник
Еще один не охваченный вопрос - передача параметров для инициализации классов. Поскольку назначение
super
зависит от подкласса, единственный хороший способ передать параметры - это собрать их все вместе. Тогда будьте осторожны, чтобы не иметь одинаковое имя параметра с разными значениями.Пример:
дает:
Вызов суперкласса
__init__
напрямую для более непосредственного присвоения параметров заманчив, но не получается, если естьsuper
в суперклассе вызов, и / или MRO изменяется, и класс A может вызываться несколько раз, в зависимости от реализации.В заключение: кооперативное наследование, супер и специфические параметры для инициализации не очень хорошо работают вместе.
источник
Выход
Call to Third () находит инициат, определенный в Third. И вызов super в этой подпрограмме вызывает init, определенный в First. MRO = [Первый, Второй]. Теперь вызов super в init, определенном в First, продолжит поиск MRO и найдет init, определенный в Second, и любой вызов super попадет в объект init по умолчанию. . Я надеюсь, что этот пример проясняет концепцию.
Если вы не позвоните супер из Первого. Цепочка останавливается, и вы получите следующий вывод.
источник
В learningpyhonthehardway я изучаю нечто, называемое super () встроенной функцией, если не ошибаюсь. Вызов функции super () может помочь наследованию пройти через родителя и «братьев и сестер» и помочь вам увидеть яснее. Я все еще новичок, но я люблю делиться своим опытом использования этого super () в python2.7.
Если вы прочитали комментарии на этой странице, вы услышите о Порядке разрешения методов (MRO), метод, который является функцией, которую вы написали, MRO будет использовать схему Depth-First-слева-направо для поиска и запуска. Вы можете сделать больше исследований по этому вопросу.
Добавляя функцию super ()
Вы можете соединить несколько экземпляров и «семей» с помощью super (), добавив в них все и каждого из них. И он будет выполнять методы, пройти их и убедиться, что вы не пропустили! Однако, добавив их до или после, вы узнаете, выполнили ли вы упражнение learningpythonthehardway 44. Пусть начнется веселье !!
Взяв пример ниже, вы можете скопировать и вставить и попробовать запустить его:
Как это работает? Экземпляр five () будет выглядеть следующим образом. Каждый шаг идет от класса к классу, где добавлена супер функция.
Родитель был найден, и он будет продолжаться до третьего и четвертого !!
Теперь все классы с super () были доступны! Родительский класс был найден и выполнен, и теперь он продолжает распаковывать функцию в наследствах для завершения кодов.
Результат программы выше:
Для меня добавление super () позволяет мне увидеть, как python будет выполнять мое кодирование, и убедиться, что наследование может получить доступ к методу, который я намеревался.
источник
Я хотел бы добавить к тому, что @Visionscaper говорит вверху:
В этом случае интерпретатор не отфильтровывает объектный класс, потому что он дублирован, а скорее потому, что Second появляется в позиции головы и не появляется в хвостовой позиции в подмножестве иерархии. Пока объект появляется только в хвостовых позициях и не считается сильной позицией в алгоритме C3 для определения приоритета.
Линеаризация (MRO) класса C, L (C), является
Линейное слияние выполняется путем выбора общих классов, которые появляются в качестве заголовка списков, а не хвоста, поскольку порядок имеет значение (станет ясно ниже)
Линеаризация третьего может быть вычислена следующим образом:
Таким образом, для реализации super () в следующем коде:
становится очевидным, как этот метод будет решен
источник
В Python 3.5+ наследование выглядит предсказуемо и очень приятно для меня. Пожалуйста, посмотрите на этот код:
Выходы:
Как видите, он вызывает foo ровно ОДНО время для каждой унаследованной цепочки в том же порядке, в котором она была унаследована. Вы можете получить этот заказ, позвонив . мро :
Четвертый -> Третий -> Первый -> Второй -> База -> объект
источник
Возможно, есть еще что-то, что можно добавить, небольшой пример с Django rest_framework и декораторами. Это дает ответ на скрытый вопрос: «зачем мне это все равно?»
Как уже было сказано: мы с Django rest_framework, и мы используем общие представления, и для каждого типа объектов в нашей базе данных мы находимся с одним классом представления, предоставляющим GET и POST для списков объектов, и другим классом представления, предоставляющим GET , PUT и DELETE для отдельных объектов.
Теперь POST, PUT и DELETE мы хотим украсить Django login_required. Обратите внимание, как это касается обоих классов, но не всех методов в обоих классах.
Решение может пройти множественное наследование.
Аналогично для других методов.
В список наследования моих конкретных классов я бы добавил свои
LoginToPost
доListCreateAPIView
иLoginToPutOrDelete
доRetrieveUpdateDestroyAPIView
. Мои конкретные занятияget
остались бы без декорации.источник
Размещение этого ответа для моей будущей референции.
Множественное наследование Python должно использовать алмазную модель, и сигнатура функции не должна изменяться в модели.
Пример кода будет:
Здесь класс А есть
object
источник
A
должны также быть вызовом__init__
.A
не «изобрел» метод__init__
, поэтому он не может предположить, что какой-то другой класс мог иметьA
ранее в своем MRO. Единственный класс, чей__init__
метод не вызывает (и не должен) вызыватьsuper().__init__
этоobject
.object
может быть, я думаю, я должен написатьclass A (object) :
вместо этогоA
не может быть,object
если вы добавляете параметр к его__init__
.