Представьте себе следующие отношения:
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”