namedtuple vs NamedTuple: почему одно содержит __annotations__, а другое — нет
@python_quizРазберем этот квиз
Коротко: небольшой фрагмент кода проверяет наличие атрибута __annotations__ у экземпляров двух похожих структур данных. Результат показывает, что один вариант создаёт аннотации полей, другой — нет. Разберёмся, почему так происходит и что это значит на практике.
Код, который вызывает вопрос

Когда вы запускаете этот код, он выведет:

Разбор: как это работает
- collections.namedtuple создаёт класс на лету, даёт ему удобные имена полей, методы вроде _asdict(), _replace() и т.д. Но он не добавляет атрибут
__annotations__в класс, поскольку не опирается на синтаксис аннотаций типов. - typing.NamedTuple — это специальный инструмент для декларации именованных кортежей с использованием синтаксиса аннотаций. При объявлении классов через typing.NamedTuple аннотированные атрибуты попадают в словарь
__annotations__класса.
Важно: проверка производится через hasattr(экземпляр, '__annotations__'). При поиске атрибута на экземпляре Python также смотрит на его класс. Поэтому если класс имеет __annotations__, hasattr(instance, '__annotations__') вернёт True.
Демонстрация свойств и аннотаций
Посмотрим на поведение чуть подробнее:

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

Практические выводы
- Если вам важна поддержка аннотаций типов (для статического анализа, автодополнения IDE, сериализации с учётом типов и т.п.), используйте typing.NamedTuple или dataclasses.dataclass.
- Если нужен просто компактный, быстрый способ определить именованные кортежи без аннотаций — collections.namedtuple остаётся простым и эффективным выбором.
- Проверка hasattr(instance, '
__annotations__') может вернуть True и для экземпляра, если класс имеет это поле — это нормальное поведение механизмов разрешения атрибутов в Python.
Когда выбирать что
- Нужны аннотации/совместимость с typing — выбирайте typing.NamedTuple или dataclasses (в зависимости от иммутабельности).
- Нужна совместимость с более старым кодом или минимализм — collections.namedtuple подойдёт.
Надеюсь, краткое сравнение разъяснило, почему в примере один тип возвращает True для __annotations__, а другой — False, и как это влияет на использование в реальных проектах.