Метакласс и атрибуты: кто получает created?

Метакласс и атрибуты: кто получает created?

@python_quiz

Разберем этот квиз

Коротко: код использует метакласс, который переопределяет __call__. При создании экземпляра метакласс добавляет атрибут created именно в объект-экземпляр, поэтому вывод будет: False True.

Ниже подробно разберём, почему так происходит, и покажем альтернативы.

Исходный код

python

Запуск этого кода выводит:

Пошаговое объяснение

  1. Определён метакласс M, который наследует от type и переопределяет метод __call__.
  2. В вызове B() интерпретатор вызывает M.__call__(B, ...). То есть __call__ метакласса отвечает за создание экземпляра класса B.
  3. Внутри M.__call__ сначала вызывается super().__call__(*a, **k). Это стандартное поведение — создание экземпляра (выделение памяти и вызов A.__init__).
  4. После создания экземпляра код устанавливает obj.created = True — атрибут устанавливается в самом экземпляре.
  5. Возвращается экземпляр obj. Следовательно:

- hasattr(b, 'created') — True (атрибут есть в экземпляре).
- hasattr(B, 'created') — False (атрибут не установлен на самом классе B).

То есть метакласс управляет процессом создания экземпляров, но в данном коде модификация направлена на объект, а не на класс.

Анализ вариантов ответов (логика)

  • "True True": произошло бы, если бы и класс, и экземпляр имели атрибут created. В текущем коде атрибут ставится только у экземпляра, потому этот вариант неверен.
  • "False True": соответствует описанному поведению: класс не получает created, экземпляр получает — это именно наблюдаемый вывод.
  • "True False": означало бы, что created добавлен к классу, но не к экземпляру. При установке attr на класс (например, cls.created = True) такой результат возможен, но не в данном коде.
  • "False False": означало бы, что ни класс, ни экземпляр не получили created — не соответствует действительности, потому что экземпляр явно получает атрибут.

Варианты изменения поведения

  1. Сделать так, чтобы атрибут был у класса:
python

Обратите внимание: после установки cls.created = True класс B будет иметь created, и hasattr(b, 'created') тоже вернёт True, потому что при поиске атрибутов Python смотрит в __dict__ экземпляра, затем в классе.

  1. Установить атрибут во время создания класса (в метаклассе __new__ или __init__ метакласса):
python

Это способ влиять на сами классы при их определении, а не при создании их экземпляров.

Короткий вывод

Переопределение __call__ в метаклассе даёт точку управления при создании экземпляров. В приведённом коде метакласс добавляет атрибут к самому экземпляру, а не к классу, поэтому hasattr(B, 'created') возвращает False, а hasattr(b, 'created') — True. Этот паттерн полезен, когда нужно инициализировать или пометить экземпляры централизованно при их создании.

Report Page