Named Tuples

As with tuples, named tuple classes require some special behaviors for type checking.

Defining Named Tuples

A named tuple class can be defined using a class syntax or a factory function call.

Type checkers should support the class syntax:

class Point(NamedTuple):
    x: int
    y: int
    units: str = "meters"

Regardless of whether the class syntax or factory function call is used to define a named tuple, type checkers should synthesize a __new__ method based on the named tuple fields. This mirrors the runtime behavior. In the example above, the synthesized __new__ method would look like the following:

def __new__(cls, x: int, y: int, units: str = "meters") -> Self:
    ...

The runtime implementation of NamedTuple enforces that fields with default values must come after fields without default values. Type checkers should likewise enforce this restriction:

class Location(NamedTuple):
    altitude: float = 0.0
    latitude: float  # Type error (previous field has a default value)
    longitude: float

A named tuple class can be subclassed, but any fields added by the subclass are not considered part of the named tuple type. Type checkers should enforce that these newly-added fields do not conflict with the named tuple fields in the base class:

class PointWithName(Point):
    name: str  # OK
    x: int  # Type error (invalid override of named tuple field)

In Python 3.11 and newer, the class syntax supports generic named tuple classes. Type checkers should support this:

class Property[T](NamedTuple):
    name: str
    value: T

reveal_type(Property("height", 3.4))  # Revealed type is Property[float]

NamedTuple does not support multiple inheritance. Type checkers should enforce this limitation:

class Unit(NamedTuple, object):  # Type error
    name: str

The factory function call supports two variants: collections.namedtuple and typing.NamedTuple. The latter provides a way to specify the types of the fields in the tuple whereas the former does not. The namedtuple form allows fields to be specified as a tuple or list of strings or a single string with fields separated by whitespace or commas. The NamedTuple functional form accepts an iterable of (name, type) pairs. For the namedtuple form, all fields are assumed to be of type Any.

A type checker may support the factory function call in its various forms:

Point1 = namedtuple('Point1', ['x', 'y'])
Point2 = namedtuple('Point2', ('x', 'y'))
Point3 = namedtuple('Point3', 'x y')
Point4 = namedtuple('Point4', 'x, y')

Point5 = NamedTuple('Point5', [('x', int), ('y', int)])
Point6 = NamedTuple('Point6', (('x', int), ('y', int)))

At runtime, the namedtuple function disallows field names that are illegal Python identifiers and either raises an exception or replaces these fields with a parameter name of the form _N. The behavior depends on the value of the rename argument. Type checkers may replicate this behavior statically:

NT1 = namedtuple("NT1", ["a", "a"])  # Type error (duplicate field name)
NT2 = namedtuple("NT2", ["abc", "def"], rename=False)  # Type error (illegal field name)

NT3 = namedtuple("NT3", ["abc", "def"], rename=True)  # OK
NT3(abc="", _1="")  # OK

The namedtuple function also supports a defaults keyword argument that specifies default values for the fields. Type checkers may support this:

NT4 = namedtuple("NT4", "a b c", defaults=(1, 2))
NT4()  # Type error (too few arguments)
NT4(1)  # OK

Named Tuple Usage

The fields within a named tuple instance can be accessed by name using an attribute access (.) operator. Type checkers should support this:

p = Point(1, 2)
assert_type(p.x, int)
assert_type(p.units, str)

Like normal tuples, elements of a named tuple can also be accessed by index, and type checkers should support this:

assert_type(p[0], int)
assert_type(p[2], str)

Type checkers should enforce that named tuple fields cannot be overwritten or deleted:

p.x = 3  # Type error
p[0] = 3  # Type error
del p.x  # Type error
del p[0]  # Type error

Like regular tuples, named tuples can be unpacked. Type checkers should understand this:

x, y, units = p
assert_type(x, int)
assert_type(units, str)

x, y = p  # Type error (too few values to unpack)

Type Compatibility Rules

A named tuple is a subtype of a tuple with a known length and parameterized by types corresponding to the named tuple’s individual field types:

p = Point(x=1, y=2, units="inches")
v1: tuple[int, int, str] = p  # OK
v2: tuple[Any, ...] = p  # OK
v3: tuple[int, int] = p  # Type error (too few elements)
v4: tuple[int, str, str] = p  # Type error (incompatible element type)

As with normal tuples, named tuples are covariant in their type parameters:

v5: tuple[float, float, str] = p  # OK