|
| 1 | +# `NamedTuple` |
| 2 | + |
| 3 | +`NamedTuple` is a type-safe way to define named tuples — a tuple where each field can be accessed by |
| 4 | +name, and not just by its numeric position within the tuple: |
| 5 | + |
| 6 | +## `typing.NamedTuple` |
| 7 | + |
| 8 | +### Basics |
| 9 | + |
| 10 | +```py |
| 11 | +from typing import NamedTuple |
| 12 | + |
| 13 | +class Person(NamedTuple): |
| 14 | + id: int |
| 15 | + name: str |
| 16 | + age: int | None = None |
| 17 | + |
| 18 | +alice = Person(1, "Alice", 42) |
| 19 | +alice = Person(id=1, name="Alice", age=42) |
| 20 | +bob = Person(2, "Bob") |
| 21 | +bob = Person(id=2, name="Bob") |
| 22 | + |
| 23 | +reveal_type(alice.id) # revealed: int |
| 24 | +reveal_type(alice.name) # revealed: str |
| 25 | +reveal_type(alice.age) # revealed: int | None |
| 26 | + |
| 27 | +# TODO: These should reveal the types of the fields |
| 28 | +reveal_type(alice[0]) # revealed: Unknown |
| 29 | +reveal_type(alice[1]) # revealed: Unknown |
| 30 | +reveal_type(alice[2]) # revealed: Unknown |
| 31 | + |
| 32 | +# error: [missing-argument] |
| 33 | +Person(3) |
| 34 | + |
| 35 | +# error: [too-many-positional-arguments] |
| 36 | +Person(3, "Eve", 99, "extra") |
| 37 | + |
| 38 | +# error: [invalid-argument-type] |
| 39 | +Person(id="3", name="Eve") |
| 40 | +``` |
| 41 | + |
| 42 | +Alternative functional syntax: |
| 43 | + |
| 44 | +```py |
| 45 | +Person2 = NamedTuple("Person", [("id", int), ("name", str)]) |
| 46 | +alice2 = Person2(1, "Alice") |
| 47 | + |
| 48 | +# TODO: should be an error |
| 49 | +Person2(1) |
| 50 | + |
| 51 | +reveal_type(alice2.id) # revealed: @Todo(GenericAlias instance) |
| 52 | +reveal_type(alice2.name) # revealed: @Todo(GenericAlias instance) |
| 53 | +``` |
| 54 | + |
| 55 | +### Multiple Inheritance |
| 56 | + |
| 57 | +Multiple inheritance is not supported for `NamedTuple` classes: |
| 58 | + |
| 59 | +```py |
| 60 | +from typing import NamedTuple |
| 61 | + |
| 62 | +# This should ideally emit a diagnostic |
| 63 | +class C(NamedTuple, object): |
| 64 | + id: int |
| 65 | + name: str |
| 66 | +``` |
| 67 | + |
| 68 | +### Inheriting from a `NamedTuple` |
| 69 | + |
| 70 | +Inheriting from a `NamedTuple` is supported, but new fields on the subclass will not be part of the |
| 71 | +synthesized `__new__` signature: |
| 72 | + |
| 73 | +```py |
| 74 | +from typing import NamedTuple |
| 75 | + |
| 76 | +class User(NamedTuple): |
| 77 | + id: int |
| 78 | + name: str |
| 79 | + |
| 80 | +class SuperUser(User): |
| 81 | + level: int |
| 82 | + |
| 83 | +# This is fine: |
| 84 | +alice = SuperUser(1, "Alice") |
| 85 | +reveal_type(alice.level) # revealed: int |
| 86 | + |
| 87 | +# This is an error because `level` is not part of the signature: |
| 88 | +# error: [too-many-positional-arguments] |
| 89 | +alice = SuperUser(1, "Alice", 3) |
| 90 | +``` |
| 91 | + |
| 92 | +### Generic named tuples |
| 93 | + |
| 94 | +```toml |
| 95 | +[environment] |
| 96 | +python-version = "3.12" |
| 97 | +``` |
| 98 | + |
| 99 | +```py |
| 100 | +from typing import NamedTuple |
| 101 | + |
| 102 | +class Property[T](NamedTuple): |
| 103 | + name: str |
| 104 | + value: T |
| 105 | + |
| 106 | +# TODO: this should be supported (no error, revealed type of `Property[float]`) |
| 107 | +# error: [invalid-argument-type] |
| 108 | +reveal_type(Property("height", 3.4)) # revealed: Property[Unknown] |
| 109 | +``` |
| 110 | + |
| 111 | +## `collections.namedtuple` |
| 112 | + |
| 113 | +```py |
| 114 | +from collections import namedtuple |
| 115 | + |
| 116 | +Person = namedtuple("Person", ["id", "name", "age"], defaults=[None]) |
| 117 | + |
| 118 | +alice = Person(1, "Alice", 42) |
| 119 | +bob = Person(2, "Bob") |
| 120 | +``` |
0 commit comments