NamedTuple vs collections.namedtuple — где появляется __annotations__?
@python_quizРазберем этот квиз
Коротко: в приведённом коде при проверке hasattr на экземплярах первая проверка вернёт False, а вторая — True. Ниже покажу почему, разберу механизм доступа к атрибутам и продемонстрирую дополнительные проверки.
Исходный код

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

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

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

Почему 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__нет.