Мне неясно, что именно представляет собой ваш вопрос, поэтому было бы полезно, если бы вы могли точно объяснить, что вы ожидали, и как это отличалось от того, что произошло на самом деле.В свете этого я попытаюсь объяснить, как MRO оценивается здесь.
Во-первых, поскольку иерархия классов в примере кода довольно запутана, это может помочь визуализировать структуру наследования:
Обращаясь к вашему вопросу,
Соблюдает ли init и set обе цепочки вызовов mro?
Если я правильно понимаю, короткий ответ - да.MRO определяется на основе наследования классов и является атрибутом классов , а не методов.Ваш цикл по SizedRegexString.__mro__
иллюстрирует этот факт, поэтому я предполагаю, что ваш вопрос возник из-за несоответствия между цепочками вызовов __init__
и __set__
.
__ init__ Цепочка вызовов
Цепочка вызовов для SizedRegexString.__init__
выглядит следующим образом:
SizedRegexString.__init__
, который явно не определен, поэтому он откладывается до своегоопределение суперкласса SizedString.__init__
, которое явно не определено String.__init__
, которое не определено явно Typed.__init__
, которое не определено явно Sized.__init__
, который устанавливает maxlen
, затем вызывает super().__init__()
Regex.__init__
, который устанавливает pat
, затем вызывает super().__init__()
Descriptor.__init__
, которыйустанавливает name
Таким образом, при вызове SizedRegexString.__init__
, согласно MRO, существует семь определенных классов, которые необходимо проверить на метод __init__
(при условии, что каждый вызов super().__init__()
, какЧто ж).Однако, как вы заметили, выходные данные операторов print внутри методов __init__
показывают, что посещаются следующие классы: Sized
, Regex
и Descriptor
.Обратите внимание, что это те же классы - в том же порядке, что и те, которые упомянуты в пулях выше как явно определенные.
Итак, для нас это может показаться похожим на MRO для SizedRegexString
[Sized
, Regex
, Descriptor
], потому что это только три класса, которые мы видимна самом деле делать вещи.Однако, это не так.Приведенная выше маркированная MRO все еще соблюдается, но ни один из классов до Sized
явно не определяет метод __init__
, поэтому каждый из них молча откладывает свои суперклассы.
__ set__ Call Chain
Это объясняет, как __init__
следует за MRO, но почему __set__
ведет себя по-другому?Чтобы ответить на этот вопрос, мы можем следовать той же маркированной MRO, использованной выше:
SizedRegexString.__set__
, которая явно не определена, поэтому она подчиняется определению своего суперкласса SizedString.__set__
,который явно не определен String.__set__
, который явно не определен Typed.__set__
, который проверяет, что value
является экземпляром self.ty
, затем вызывает super().__set__()
Sized.__set__
, который проверяет длину value
, затем вызывает super().__set__()
Regex.__set__
, что обеспечивает соответствие между self.pat
и value
, затем вызывает super().__set__()
Descriptor.__set__
, который добавляет пару ключ / значение self.name
и value
к instance.__dict__
Вывод здесь заключается в том, что __set__
придерживаетсято же MRO, что и __init__
, потому что они принадлежат к одному и тому же классу, хотя в этот раз мы видим активность четырех разных классов, тогда как мы видели только три с __init__
.Итак, еще раз, может показаться , как будто MRO SizedRegexString
теперь [Typed
, Sized
, Regex
, Descriptor
].Это может сбивать с толку, поскольку эта новая цепочка вызовов отличается как от SizedRegexString.__mro__
, так и от кажущейся цепочки вызовов, которую мы видели для SizedRegexString.__init__
.
TL; DR
Но после следования цепочкам вызовов для __init__
и __set__
, мы можем видеть, что они оба следуют MRO.Несоответствие происходит из-за того, что больше потомков Descriptor
явно определяют метод __set__
, чем метод __init__
.
Дополнительные точки
Вот еще пара моментов, которые могут бытьвызывая некоторую путаницу:
Ни один из определенных __set__
методов фактически не вызывается в текущем состоянии кода вашего примера.Мы можем выяснить, почему из следующих двух строк из вашего примера кода:
class Stock(Structure):
name = SizedRegexString(maxlen=8, pat=“[A-Z]+$”)
Конечный продукт этих двух строк (Stock
) создается методом __new__
метакласса StructMeta
.Хотя Stock
имеет атрибут класса name
, который является экземпляром SizedRegexString
, атрибуты этого экземпляра не устанавливаются.Следовательно, ни один из методов __set__
не вызывается.Мы ожидаем, что вызов __set__
будет в Stock.__init__
, из-за следующих строк в Structure.__init__
:
for n, v in bound.arguments.items():
setattr(self, n, v)
Добавив s = Stock(name=“FOO”)
в конец кода вашего примера, мы можемсм. методы __set__
, выполняющиеся успешно.Кроме того, мы можем проверить, что правильные ошибки возникают на Regex.__set__
и Sized.__set__
с s = Stock(name=“foo”)
и s = Stock(name=“FOOFOOFOO”)
соответственно
После Python 3.6, dict
sупорядочено по умолчанию, поэтому метод __prepare__
в StructMeta
может оказаться излишним, в зависимости от того, какую версию Python вы используете
Надеюсь, я рассмотрел ваш вопрос.Если бы я полностью упустил момент, я был бы рад попробовать еще раз, если бы вы могли уточнить, что именно вы ожидаете.