namedtuple vs typing.NamedTuple — почему типы разные, но оба tuple

namedtuple vs typing.NamedTuple — почему типы разные, но оба tuple

@python_quiz

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

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

Условие (исходный код)

python

Что делает код — по шагам

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

Результат выполнения

Если запустить код, он печатает:

Интерпретация вывода:

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

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

Чтобы убедиться и увидеть подробности типов, можно расширить вывод:

python

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

Почему типы разные (коротко и ясно)

  • collections.namedtuple создаёт класс программно внутри функции — это класс, но он отличается по реализации и имени от класса, созданного через декларативный синтаксис typing.NamedTuple.
  • typing.NamedTuple использует механизмы аннотаций и специальный метакласс, поэтому класс, который получается, технически отличается от того, что вернёт namedtuple. Следовательно, объекты принадлежат разным классам.

Практические замечания и отличия

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

Вывод

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

Report Page