NamedTuple vs collections.namedtuple — где появляется __annotations__?

NamedTuple vs collections.namedtuple — где появляется __annotations__?

@python_quiz

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

Коротко: в приведённом коде при проверке hasattr на экземплярах первая проверка вернёт False, а вторая — True. Ниже покажу почему, разберу механизм доступа к атрибутам и продемонстрирую дополнительные проверки.

Исходный код

python

При запуске этот код выведет:

Объяснение — откуда берётся __annotations__

  • typing.NamedTuple создаёт класс, в котором записаны аннотации полей в атрибуте класса __annotations__. Этот атрибут — обычный атрибут класса, и при обращении к нему через экземпляр (например, hasattr(instance, '__annotations__')) поиск находит класс-атрибут и возвращает True.
  • collections.namedtuple — это фабрика, которая динамически создаёт класс, наследующий от tuple, но она не заполняет __annotations__ таким образом, как делает это typing.NamedTuple. Поэтому ни на классе, ни на экземпляре такого атрибута нет.

Демонстрация и дополнительные проверки

Давайте явно посмотрим, где находится __annotations__ и как оно выглядит.

python

Ожидаемый вывод:

Почему hasattr(instance, '__annotations__') находит атрибут класса?

Механизм поиска атрибутов в Python: при обращении instance.attr сначала проверяется словарь экземпляра, затем словари классов (и далее по MRO). Поскольку __annotations__ у NamedTuple — это атрибут класса, hasattr на экземпляре возвращает True. У классов, созданных через collections.namedtuple, такого атрибута нет.

Практические замечания и выводы

  • Если вам важны статические аннотации полей (и вы хотите, чтобы они присутствовали в __annotations__), используйте typing.NamedTuple (или dataclass с классическими аннотациями).
  • collections.namedtuple удобен, когда нужен лёгкий immutable-кортеж с именованными полями и небольшим накладным кодом, но он не создаёт стандартных аннотаций полей в __annotations__.
  • Для инструментов, которые опираются на аннотации (type checkers, сериализаторы, автогенерация схем), наличие __annotations__ у класса может быть критичным.

Короткая шпаргалка:

  • typing.NamedTuple: аннотации есть, видны через класс и через экземпляр.
  • collections.namedtuple: аннотаций в __annotations__ нет.

Report Page