namedtuple vs typing.NamedTuple — почему типы разные, но оба tuple
@python_quizРазберем этот квиз
Коротко: код сравнивает типы двух похожих структур — одну созданную через collections.namedtuple, другую через typing.NamedTuple — и проверяет, является ли один из экземпляров tuple. Результат может удивить, если не знать различий в механизмах создания типов.
Условие (исходный код)

Что делает код — по шагам
- collections.namedtuple — это фабрика функций, которая динамически создаёт класс (подкласс tuple) с указанными полями.
- typing.NamedTuple при объявлении через синтаксис
class ...(NamedTuple)создаёт класс с аннотациями, который также является подклассом tuple, но формируется другим механизмом (через специальный метакласс и поддержку подсказок типов). - u1 и u2 — экземпляры двух разных классов, но оба класса наследуют tuple.
Результат выполнения
Если запустить код, он печатает:

Интерпретация вывода:
- Первое значение (False) — сравнение объектов типов через
is: типы u1 и u2 — не один и тот же объект (разные классы). - Второе значение (True) —
u2является экземпляром tuple, поскольку класс User2 наследует tuple.
Дополнительная демонстрация и проверки
Чтобы убедиться и увидеть подробности типов, можно расширить вывод:

Ожидаемый вывод (формат может отличаться по представлению имени класса):

Почему типы разные (коротко и ясно)
- collections.namedtuple создаёт класс программно внутри функции — это класс, но он отличается по реализации и имени от класса, созданного через декларативный синтаксис typing.NamedTuple.
- typing.NamedTuple использует механизмы аннотаций и специальный метакласс, поэтому класс, который получается, технически отличается от того, что вернёт namedtuple. Следовательно, объекты принадлежат разным классам.
Практические замечания и отличия
- Оба варианта дают неизменяемые (tuple-подобные) объекты с доступом по именованным полям.
- typing.NamedTuple удобен при использовании аннотаций типов, статического анализа (mypy) и читабельности кода.
- collections.namedtuple полезен, если нужен лёгкий способ динамически создать класс (например, на основе строки с полями или в рантайме).
- Внутренности реализации и поведение методов могут немного различаться (например, сколько вспомогательных атрибутов создаётся, как формируется
__repr__, и т.д.).
Вывод
Обе конструкции создают подклассы tuple — поэтому isinstance для экземпляров этих классов относительно tuple возвращает True. Однако классы, создаваемые через collections.namedtuple и через typing.NamedTuple, — разные объекты, поэтому сравнение их типов через is даёт False. Этот факт полезно помнить при проверках типов и при разработке, где важна именно идентичность класса, а не наследование.