3.8. Typing NamedTuple
NamedTuple is a class that allows you to create a tuple with named fields.
NamedTuple is immutable.
3.8.1. SetUp
>>> from typing import NamedTuple
3.8.2. Problem
>>> a: tuple = ('Mark', 'Watney', 41)
>>> b: tuple[str,str,int] = ('Mark', 'Watney', 41)
3.8.3. Solution
>>> class User(NamedTuple):
... firstname: str
... lastname: str
... age: int
>>>
>>>
>>> a: User = User('Mark', 'Watney', 41)
>>> b: User = User(firstname='Mark', lastname='Watney', age=41)
3.8.4. Default
>>> class User(NamedTuple):
... firstname: str
... lastname: str
... age: int = None
>>>
>>>
>>> a: User = User(firstname='Mark', lastname='Watney', age=41)
>>> b: User = User(firstname='Mark', lastname='Watney')
3.8.5. Field Access
You can access fields by name or index.
>>> class User(NamedTuple):
... firstname: str
... lastname: str
>>>
>>>
>>> mark: User = User(firstname='Mark', lastname='Watney')
>>>
>>> mark.firstname
'Mark'
>>>
>>> mark.lastname
'Watney'
3.8.6. Is Instance
NamedTuple is a subclass of tuple
>>> class User(NamedTuple):
... firstname: str
... lastname: str
>>>
>>>
>>> mark: User = User(firstname='Mark', lastname='Watney')
>>>
>>> isinstance(mark, User)
True
>>>
>>> isinstance(mark, tuple)
True
3.8.7. Use Case - 1
>>> class Point(NamedTuple):
... x: int
... y: int
... z: int = 0
>>>
>>> a = Point(x=1, y=2)
>>> b = Point(x=1, y=2, z=3)
3.8.8. Use Case - 2
Point 2d:
>>> class Point(NamedTuple):
... x: int
... y: int
>>>
>>> pt = Point(x=1, y=2)
Point 3d:
>>> class Point(NamedTuple):
... x: int
... y: int
... z: int = 0
>>>
>>> pt = Point(x=1, y=2)
3.8.9. Use Case - 3
>>> class User(NamedTuple):
... id: int
... firstname: str
... lastname: str
... age: int
... height: int | float
... weight: int | float
... is_astronaut: bool
... is_assigned: bool
... mission: str | None
3.8.10. Use Case - 4
>>> class Point(NamedTuple):
... x: int = 0
... y: int = 0
>>>
>>>
>>> class Position:
... position: Point
...
... def __init__(self, initial_position: Point = Point()):
... self.position = initial_position
...
... def set_position(self, position: Point) -> None:
... self.position = position
...
... def get_position(self) -> Point:
... return self.position
>>>
>>>
>>> current = Position()
>>>
>>> current.get_position()
Point(x=0, y=0)
>>>
>>> current.set_position(Point(1, 2))
>>>
>>> current.get_position()
Point(x=1, y=2)
3.8.11. Use Case - 5
>>> class GeographicCoordinate(NamedTuple):
... latitude: float
... longitude: float
>>>
>>>
>>> locations: list[tuple[float,float]] = [
... (25.91375, -60.15503),
... (-11.01983, -166.48477),
... (-11.01983, -166.48477),
... ]
>>>
>>> locations: list[GeographicCoordinate] = [
... GeographicCoordinate(25.91375, -60.15503),
... GeographicCoordinate(-11.01983, -166.48477),
... GeographicCoordinate(-11.01983, -166.48477),
... ]
>>>
>>> locations: list[GeographicCoordinate] = [
... GeographicCoordinate(latitude=25.91375, longitude=-60.15503),
... GeographicCoordinate(latitude=-11.01983, longitude=-166.48477),
... GeographicCoordinate(latitude=-11.01983, longitude=-166.48477),
... ]
3.8.12. Use Case - 6
>>> from itertools import starmap
>>> from pprint import pprint
>>>
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'setosa'),
... (7.0, 3.2, 4.7, 1.4, 'versicolor'),
... (7.6, 3.0, 6.6, 2.1, 'virginica'),
... (4.9, 3.0, 1.4, 0.2, 'setosa'),
... (4.9, 2.5, 4.5, 1.7, 'virginica'),
... (7.1, 3.0, 5.9, 2.1, 'virginica'),
... (4.6, 3.4, 1.4, 0.3, 'setosa'),
... (5.4, 3.9, 1.7, 0.4, 'setosa'),
... (5.7, 2.8, 4.5, 1.3, 'versicolor'),
... (5.0, 3.6, 1.4, 0.3, 'setosa'),
... (5.5, 2.3, 4.0, 1.3, 'versicolor'),
... (6.5, 3.0, 5.8, 2.2, 'virginica'),
... (6.5, 2.8, 4.6, 1.5, 'versicolor'),
... (6.3, 3.3, 6.0, 2.5, 'virginica'),
... (6.9, 3.1, 4.9, 1.5, 'versicolor'),
... (4.6, 3.1, 1.5, 0.2, 'setosa'),
... ]
>>> class Iris(NamedTuple):
... sl: float
... sw: float
... pl: float
... pw: float
... species: str
>>> result = starmap(Iris, DATA[1:])
>>> data = list(result)
>>> pprint(data)
[Iris(sl=5.8, sw=2.7, pl=5.1, pw=1.9, species='virginica'),
Iris(sl=5.1, sw=3.5, pl=1.4, pw=0.2, species='setosa'),
Iris(sl=5.7, sw=2.8, pl=4.1, pw=1.3, species='versicolor'),
Iris(sl=6.3, sw=2.9, pl=5.6, pw=1.8, species='virginica'),
Iris(sl=6.4, sw=3.2, pl=4.5, pw=1.5, species='versicolor'),
Iris(sl=4.7, sw=3.2, pl=1.3, pw=0.2, species='setosa'),
Iris(sl=7.0, sw=3.2, pl=4.7, pw=1.4, species='versicolor'),
Iris(sl=7.6, sw=3.0, pl=6.6, pw=2.1, species='virginica'),
Iris(sl=4.9, sw=3.0, pl=1.4, pw=0.2, species='setosa'),
Iris(sl=4.9, sw=2.5, pl=4.5, pw=1.7, species='virginica'),
Iris(sl=7.1, sw=3.0, pl=5.9, pw=2.1, species='virginica'),
Iris(sl=4.6, sw=3.4, pl=1.4, pw=0.3, species='setosa'),
Iris(sl=5.4, sw=3.9, pl=1.7, pw=0.4, species='setosa'),
Iris(sl=5.7, sw=2.8, pl=4.5, pw=1.3, species='versicolor'),
Iris(sl=5.0, sw=3.6, pl=1.4, pw=0.3, species='setosa'),
Iris(sl=5.5, sw=2.3, pl=4.0, pw=1.3, species='versicolor'),
Iris(sl=6.5, sw=3.0, pl=5.8, pw=2.2, species='virginica'),
Iris(sl=6.5, sw=2.8, pl=4.6, pw=1.5, species='versicolor'),
Iris(sl=6.3, sw=3.3, pl=6.0, pw=2.5, species='virginica'),
Iris(sl=6.9, sw=3.1, pl=4.9, pw=1.5, species='versicolor'),
Iris(sl=4.6, sw=3.1, pl=1.5, pw=0.2, species='setosa')]
>>> data[0]
Iris(sl=5.8, sw=2.7, pl=5.1, pw=1.9, species='virginica')
>>>
>>> data[0].sl
5.8
>>> data[0].species
'virginica'
>>>
>>> tuple(data[0])
(5.8, 2.7, 5.1, 1.9, 'virginica')
3.8.13. Further Reading
More information in Type Annotations
More information in CI/CD Type Checking
3.8.14. References
3.8.15. Assignments
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Typing Annotations NamedTuple
# - Difficulty: easy
# - Lines: 4
# - Minutes: 3
# %% English
# 1. Define `User: NamedTuple` with:
# - `firstname: str`
# - `lastname: str`
# - `age: int`
# 2. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj `User: NamedTuple` z:
# - `firstname: str`
# - `lastname: str`
# - `age: int`
# 2. Uruchom doctesty - wszystkie muszą się powieść
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 5), \
'Python 3.5+ required'
>>> from typing import get_type_hints
>>> get_type_hints(User)
{'firstname': <class 'str'>, 'lastname': <class 'str'>, 'age': <class 'int'>}
>>> data: User = User(firstname='Mark', lastname='Watney', age=42)
>>> data
User(firstname='Mark', lastname='Watney', age=42)
>>> data: User = User('Mark', 'Watney', 42)
>>> data
User(firstname='Mark', lastname='Watney', age=42)
>>> data: User = ('Mark', 'Watney', 42)
>>> data
('Mark', 'Watney', 42)
"""
from typing import NamedTuple
# Define `User: NamedTuple` with:
# - `firstname: str`
# - `lastname: str`
# - `age: int`
class User:
...