Метакласс __call__ vs __new__/__init__: порядок вызовов в Python
@python_quizРазберем этот квиз
Коротко: при создании экземпляра класса сначала вызывается __call__ метакласса, а уже внутри него (если он делегирует) — __new__ и __init__ самого класса. Разберём это на примере, представленном в квизе.
Исходный код
Вот код из задания — он компактный и использует трюк с кортежем, чтобы печатать строку и вернуть результат второго выражения:

Если запустить этот код, на экран попадут три строки:
M.__call__
A.__new__
A.__init__
Теперь разберём, почему именно в таком порядке.
Разбор по шагам
- Вызов A() интерпретируется как вызов
__call__у метакласса класса A. Для классов стандартным метаклассом является type, а в нашем примере метакласс — M, у которого переопределён__call__. - Внутри M.
__call__выполняется print("M.__call__"), затем выполняется делегирование к стандартному type.__call__(cls, *a, **k). - Поведение type.
__call__— оно:
- вызывает cls.__new__(cls, *a, **k) для создания экземпляра,
- затем вызывает cls.__init__(экземпляр, *a, **k) для инициализации,
- и возвращает экземпляр.
- У класса A определён
__new__, который печатает "A.__new__" и возвращает объект через object.__new__(cls). - У класса A также определён
__init__, который печатает "A.__init__".
Следовательно, первым печатается строка из метакласса, затем — из __new__, затем — из __init__.
Почему автор кода использовал кортеж с [1]
В лямбдах в коде используется конструкция (print(...), expr)[1]. Это трюк:
- Сначала выполняется print(...), он возвращает None,
- затем вычисляется expr и возвращается как второй элемент кортежа.
Таким образом достигается эффект "напечатать и вернуть значение выражения" в одной строчке лямбды.
Более читаемый эквивалент — определение методов явно:

Этот код даёт тот же вывод и легче читается.
Анализ вариантов ответов (почему они верны или нет)
- Вариант, в котором печатаются только A.
__new__и A.__init__: неверен, потому что вызов A() сначала идёт через__call__метакласса, и у нас есть явный print в M.__call__, поэтому эта строка будет выведена первой. - Вариант, в котором печатается только M.
__call__: неверен, потому что M.__call__делегирует работу type.__call__, а тот в свою очередь вызывает__new__и__init__класса A, которые тоже печатают строки. - Вариант с тремя печатями (M.
__call__, A.__new__, A.__init__): соответствует реальному поведению кода по описанным выше причинам. - Вариант про ошибку из-за "перекрытия
__new__": неверно. Переопределение__call__метакласса само по себе не вызывает ошибки. Ошибка могла бы произойти, если бы метакласс__call__не возвращал корректный объект или не вызывал делегирование, но в представленном коде делегирование выполняется и всё корректно.
Дополнительно: что если метакласс не делегирует?
Если в M.__call__ не вызывать type.__call__ (или super().__call__), то поведение изменится — экземпляр может не создаться, __new__/__init__ не будут вызваны, либо метакласс может сам вернуть что-то другое. Пример:

Такая конструкция работает, но тогда вы теряете стандартный цикл создания экземпляра.
Вывод
При вызове класса с нестандартным метаклассом сначала вызывается __call__ метакласса. Если метакласс делегирует работу стандартному type.__call__, то внутри этого делегированного вызова будут последовательно вызваны __new__ и __init__ самого класса. В представленной задаче вывод состоит из трёх строк в порядке: M.__call__, A.__new__, A.__init__.