Представьте себе следующие отношения:
class Base: def chain(self): return 'Base' class A(Base): ... class B(Base): def chain(self): return f"{super().chain()} <- B" class C(A, B): pass class D(C): def chain(self): return f"{super().chain()} <- D"
Призыв цепь
В случае D
приведет к следующей строке:
In [1]: d.chain() Out[1]: 'Base <- B <- D'
Что произойдет, если следующий код работает?
In [2]: A.chain = A.chain
Потому что, верно? Теперь попробуйте позвонить d.chain ()
опять таки…
In [3]: d.chain() Out[3]: 'Base <- D'
Давайте наблюдаем DLS
Метод R эполюция О Рад ( MRO , порядок классов, где Python будет искать при разрешении методов и атрибутов):
In [4]: D.mro() Out[4]: [__main__.D, __main__.C, __main__.A, __main__.B, __main__.Base, object]
При звонке D.chain ()
Python будет смотреть в этот список и вернуть первый экземпляр, где цепь
присутствует как Член класса. В нашем примере D
Реализация цепь
Отказ
D.Chain
В свою очередь позвонит Super (). Цепочка ()
Отказ Что сделает Python? Это схватит следующий класс из D
и попробуйте найти цепь
В этом новом списке. C
не реализует это; не делает А.
. B
делает! Результат будет любым B.Chain
Возвращает, плюс бит "<- D"
Отказ
B.Chain
Вызывает ли Super () снова … Мы знаем, что мы делаем. База реализует его, и больше нет Super ()
звонки. Так что у нас есть Base.Chain
Возвращение «База»
, B.Chain ()
Возвращение «База <- B»
и D.chain ()
Наконец, возвращая «База <- B <- D»
Отказ
Так что происходит после того, как мы сделаем A.Chain.Chain
? Почему B.Chain ()
игнорируется? Давайте рассмотрим, что происходит с этим. Что такое А
Миро?
In [5]: A.mro() Out[5]: [__main__.A, __main__.Base, object]
Довольно просто. Что происходит, когда вы делаете А.Чина
? Python будет смотреть на А
, который не реализует это. Но База
реализует его, так что это реализация, которую он собирается использовать. Но Что происходит, когда мы делаем задание?
Python оценивает это присвоение право налево: A.Chain.Chain
Тогда означает «найти цепочку
для«, которая возвращается Base.Chain
Отказ Тогда, Python будет эффективно создать новый элемент в классе a dimed цепь
Действительно
Давайте вернемся к D’S MRO:
[__main__.D, __main__.C, __main__.A, __main__.B, __main__.Base, object]
То, как эти классы были построены, А
Члены будут проверены до B
Действительно Что это значит для постановления разрешения? Когда D.chain ()
звонки Super (). Цепочка ()
Теперь теперь будет захватить недавно добавленный член A
и назовите это. И от А
точка зрения, реализация такая же, как Base.Chain
, у кого нет Super ()
вызов! 🤯
Это происходит из-за динамической природы Python. Запись для Super ()
В документации Python Имеет удивительное описание этой проблемы и как это уникальное использование случаев из-за природы Python.
Многократная функция наследования Python, плюс ее динамическая природа, сделайте ее, чтобы реальный порядок разрешения метода и иерархии класса известны только во время выполнения и могут измениться в любое время во время срока службы приложения.
Конструкции классов должны быть Сотрудничество Отказ Приведенный выше пример имеет много вопросов, Но есть простой набор правил, которые помогают проектировать совместные классы :
- Методы, называемые Super () должны существовать
- Способы называются и их реализации необходимо иметь соответствующий аргумент подписи
- Каждое вхождение метода необходимо использовать Super ()
Точка вот что фактическая реализация метода, называемого Super ()
Известен только во время выполнения и не может быть легко определен статически.
Разработайте свои классы совместно и следите за мутациями в классах в вашем среде выполнения.
Хотите узнать больше о MRO Python Mro? Эта удивительная статья в документах Python вводит, как работает алгоритм после версии 2.3. Проверьте это Действительно
Это мой первый dev.to post. Пожалуйста, дайте мне знать, если есть что-то, что я могу сделать, чтобы сделать это лучше!
фото Адам Шмигильский на Бессмысленно
Оригинал: “https://dev.to/rbusquet/the-dynamic-nature-of-python-s-mro-22on”