namedtuple vs NamedTuple in Python: Key Differences
Python provides two primary ways to create lightweight, immutable,
tuple-like objects with named fields:
collections.namedtuple and typing.NamedTuple.
While both produce memory-efficient subclasses of standard tuples, they
differ significantly in their syntax, support for type annotations,
readability, and how they handle default values and custom methods. This
guide breaks down the technical and practical differences between the
two.
Definition Syntax
The most visible difference lies in how instances are defined:
collections.namedtuple: Uses a factory function introduced in Python 2.6. Field names are typically passed as a string separated by spaces/commas or as a list of strings:from collections import namedtuple Point = namedtuple('Point', ['x', 'y']) p = Point(1, 2)typing.NamedTuple: Introduced in Python 3.6, it uses modern class-based syntax inherited fromtyping.NamedTuple:from typing import NamedTuple class Point(NamedTuple): x: int y: int p = Point(1, 2)(Note:
typing.NamedTuplecan also be used as a factory function, but the class-based syntax is standard.)
Static Type Hinting
collections.namedtuple: Does not natively support static type hints. While you can add comments or type stubs externally, the definition itself does not store or enforce field types for tools likemypyor IDE autocompletion.typing.NamedTuple: Built specifically to integrate with PEP 484 and PEP 526 type annotations. Field types are declared directly in the class definition, allowing IDEs and static analyzers to catch type mismatches during development.
Default Values
collections.namedtuple: Python 3.7 introduced thedefaultsparameter, which accepts an iterable of default values applied from right to left:This syntax can be unintuitive when managing multiple fields with defaults.Point = namedtuple('Point', ['x', 'y', 'z'], defaults=[0]) # z defaults to 0typing.NamedTuple: Uses standard Python assignment syntax within the class body, making defaults clear and readable:class Point(NamedTuple): x: int y: int = 0 z: int = 0
Custom Methods and Docstrings
collections.namedtuple: Extending functionality requires defining custom methods outside the factory or creating an additional subclass on top of the generated named tuple.typing.NamedTuple: Because it uses class syntax, you can define methods, class variables, properties, and docstrings directly inside the body:class Vector(NamedTuple): x: float y: float def magnitude(self) -> float: return (self.x**2 + self.y**2) ** 0.5
Runtime Performance and Memory
Under the hood, both variants generate standard Python
tuple subclasses. They share the same underlying C
implementation, have an identical memory footprint (utilizing
__slots__), and perform equally fast during instantiation,
field access, and iteration.
Which One Should You Use?
In modern Python 3 development,
typing.NamedTuple is preferred. Its
class-based syntax, native support for type hints, intuitive default
values, and ability to house custom methods make it cleaner and more
maintainable. Reserve
collections.namedtuple for legacy
codebases or quick, throwaway dynamic definitions where type hints are
not required.