Какой объект в итоге связывается с именем класса после применения декоратора класса?
С именем класса связывается не обязательно исходный класс, а значение, возвращённое декоратором. Декоратор получает уже созданный объект класса и может вернуть исходный класс, его наследника, прокси или вообще объект другого типа.
Декораторы появились как способ явно преобразовывать функции и классы в момент их определения без отдельного присваивания результата. Это уменьшает шаблонный код регистрации, оборачивания и модификации объектов.
Для классов такой механизм особенно удобен при регистрации компонентов, добавлении метаданных и создании прокси. Однако возможность заменить класс полностью требует внимательно контролировать его идентичность и совместимость с остальным кодом.
Тело класса выполняется, затем метакласс создаёт объект класса, и только после этого вызывается декоратор. Если декоратор возвращает другой объект, имя класса в текущем пространстве имён начинает ссылаться именно на этот объект.
Из-за этого могут измениться результаты проверок isinstance и issubclass, поведение наследования, доступ к метаданным, сериализация и работа инструментов, использующих точную идентичность класса. Ошибка особенно вероятна, если разработчик считает, что декоратор лишь регистрирует класс, но фактически возвращает его замену.
Для одного декоратора конструкция концептуально эквивалентна следующей последовательности: сначала создаётся исходный класс, затем он передаётся декоратору, после чего результат декоратора присваивается имени класса.
В примере Report после объявления обозначает Child, а исходный класс становится его базовым классом. Метод title работает за счёт наследования, но идентичность объекта класса уже изменилась.
Декоратор класса вызывается один раз в момент выполнения объявления класса, а не при создании каждого экземпляра. Он может читать атрибуты класса, добавлять новые атрибуты или возвращать изменённый объект.
На практике безопасный декоратор регистрации обычно возвращает тот же класс, который получил на вход. Если нужна замена, следует явно документировать контракт: какой тип возвращается, сохраняются ли конструктор и метаданные, совместимы ли проверки типов и поддерживается ли сериализация.
В системе плагинов декоратор должен добавить класс отчёта в реестр. Возможны два подхода: вернуть исходный класс после регистрации или заменить его прокси-классом. Регистрация с возвратом исходного класса сохраняет идентичность, корректную работу isinstance, ожидания фабрик и большинства инструментов анализа.
Прокси может быть полезен для перехвата вызовов или ленивой инициализации, но тогда придётся отдельно решать вопросы конструктора, атрибутов, трассировки, сериализации и совместимости с аннотациями типов. Поэтому для одной лишь регистрации выбирают возврат исходного класса, а замену применяют только при доказанной необходимости.
Нет, вызов декоратора происходит после выполнения тела и создания объекта класса. Однако выражение, вычисляющее сам декоратор, может иметь побочные эффекты до выполнения тела класса, поэтому важно различать вычисление декоратора и применение декоратора.
Нет. Декоратор применяется один раз, когда интерпретатор выполняет объявление класса, например при импорте модуля или при выполнении вложенного объявления. Создание экземпляров вызывает конструктор уже полученного объекта класса, но повторно декоратор не запускает.
__init_subclass__, если декоратор вернёт новый класс-наследник?Создание нового класса-наследника само по себе запускает механизм __init_subclass__ у его базовых классов. Поэтому такой декоратор может вызвать дополнительную регистрацию или инициализацию, которой не было бы при возврате исходного класса. Это ещё одна причина не заменять класс без необходимости и учитывать побочные эффекты наследования.