Skip to content

Commit 7cf1391

Browse files
committed
Make a top-level TypedDict page (#14584)
This just moves content around (with minimal editing to make the moves make sense). TypedDict has been the target for several features, including some that are not yet documented. There was another PEP drafted today that was TypedDict themed. It's also pretty popular with users. Linking #13681
1 parent dd2c9a6 commit 7cf1391

File tree

3 files changed

+252
-253
lines changed

3 files changed

+252
-253
lines changed

docs/source/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Contents
7474
generics
7575
more_types
7676
literal_types
77+
typed_dict
7778
final_attrs
7879
metaclasses
7980

docs/source/more_types.rst

+1-253
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ More types
22
==========
33

44
This section introduces a few additional kinds of types, including :py:data:`~typing.NoReturn`,
5-
:py:func:`NewType <typing.NewType>`, ``TypedDict``, and types for async code. It also discusses
5+
:py:func:`NewType <typing.NewType>`, and types for async code. It also discusses
66
how to give functions more precise types using overloads. All of these are only
77
situationally useful, so feel free to skip this section and come back when you
88
have a need for some of them.
@@ -20,9 +20,6 @@ Here's a quick summary of what's covered here:
2020
signatures. This is useful if you need to encode a relationship between the
2121
arguments and the return type that would be difficult to express normally.
2222

23-
* ``TypedDict`` lets you give precise types for dictionaries that represent
24-
objects with a fixed schema, such as ``{'id': 1, 'items': ['x']}``.
25-
2623
* Async types let you type check programs using ``async`` and ``await``.
2724

2825
.. _noreturn:
@@ -949,252 +946,3 @@ generator type as the return type:
949946
loop = asyncio.get_event_loop()
950947
loop.run_until_complete(countdown_2("USS Enterprise", 5))
951948
loop.close()
952-
953-
954-
.. _typeddict:
955-
956-
TypedDict
957-
*********
958-
959-
Python programs often use dictionaries with string keys to represent objects.
960-
Here is a typical example:
961-
962-
.. code-block:: python
963-
964-
movie = {'name': 'Blade Runner', 'year': 1982}
965-
966-
Only a fixed set of string keys is expected (``'name'`` and
967-
``'year'`` above), and each key has an independent value type (``str``
968-
for ``'name'`` and ``int`` for ``'year'`` above). We've previously
969-
seen the ``dict[K, V]`` type, which lets you declare uniform
970-
dictionary types, where every value has the same type, and arbitrary keys
971-
are supported. This is clearly not a good fit for
972-
``movie`` above. Instead, you can use a ``TypedDict`` to give a precise
973-
type for objects like ``movie``, where the type of each
974-
dictionary value depends on the key:
975-
976-
.. code-block:: python
977-
978-
from typing_extensions import TypedDict
979-
980-
Movie = TypedDict('Movie', {'name': str, 'year': int})
981-
982-
movie: Movie = {'name': 'Blade Runner', 'year': 1982}
983-
984-
``Movie`` is a ``TypedDict`` type with two items: ``'name'`` (with type ``str``)
985-
and ``'year'`` (with type ``int``). Note that we used an explicit type
986-
annotation for the ``movie`` variable. This type annotation is
987-
important -- without it, mypy will try to infer a regular, uniform
988-
:py:class:`dict` type for ``movie``, which is not what we want here.
989-
990-
.. note::
991-
992-
If you pass a ``TypedDict`` object as an argument to a function, no
993-
type annotation is usually necessary since mypy can infer the
994-
desired type based on the declared argument type. Also, if an
995-
assignment target has been previously defined, and it has a
996-
``TypedDict`` type, mypy will treat the assigned value as a ``TypedDict``,
997-
not :py:class:`dict`.
998-
999-
Now mypy will recognize these as valid:
1000-
1001-
.. code-block:: python
1002-
1003-
name = movie['name'] # Okay; type of name is str
1004-
year = movie['year'] # Okay; type of year is int
1005-
1006-
Mypy will detect an invalid key as an error:
1007-
1008-
.. code-block:: python
1009-
1010-
director = movie['director'] # Error: 'director' is not a valid key
1011-
1012-
Mypy will also reject a runtime-computed expression as a key, as
1013-
it can't verify that it's a valid key. You can only use string
1014-
literals as ``TypedDict`` keys.
1015-
1016-
The ``TypedDict`` type object can also act as a constructor. It
1017-
returns a normal :py:class:`dict` object at runtime -- a ``TypedDict`` does
1018-
not define a new runtime type:
1019-
1020-
.. code-block:: python
1021-
1022-
toy_story = Movie(name='Toy Story', year=1995)
1023-
1024-
This is equivalent to just constructing a dictionary directly using
1025-
``{ ... }`` or ``dict(key=value, ...)``. The constructor form is
1026-
sometimes convenient, since it can be used without a type annotation,
1027-
and it also makes the type of the object explicit.
1028-
1029-
Like all types, ``TypedDict``\s can be used as components to build
1030-
arbitrarily complex types. For example, you can define nested
1031-
``TypedDict``\s and containers with ``TypedDict`` items.
1032-
Unlike most other types, mypy uses structural compatibility checking
1033-
(or structural subtyping) with ``TypedDict``\s. A ``TypedDict`` object with
1034-
extra items is compatible with (a subtype of) a narrower
1035-
``TypedDict``, assuming item types are compatible (*totality* also affects
1036-
subtyping, as discussed below).
1037-
1038-
A ``TypedDict`` object is not a subtype of the regular ``dict[...]``
1039-
type (and vice versa), since :py:class:`dict` allows arbitrary keys to be
1040-
added and removed, unlike ``TypedDict``. However, any ``TypedDict`` object is
1041-
a subtype of (that is, compatible with) ``Mapping[str, object]``, since
1042-
:py:class:`~typing.Mapping` only provides read-only access to the dictionary items:
1043-
1044-
.. code-block:: python
1045-
1046-
def print_typed_dict(obj: Mapping[str, object]) -> None:
1047-
for key, value in obj.items():
1048-
print(f'{key}: {value}')
1049-
1050-
print_typed_dict(Movie(name='Toy Story', year=1995)) # OK
1051-
1052-
.. note::
1053-
1054-
Unless you are on Python 3.8 or newer (where ``TypedDict`` is available in
1055-
standard library :py:mod:`typing` module) you need to install ``typing_extensions``
1056-
using pip to use ``TypedDict``:
1057-
1058-
.. code-block:: text
1059-
1060-
python3 -m pip install --upgrade typing-extensions
1061-
1062-
Totality
1063-
--------
1064-
1065-
By default mypy ensures that a ``TypedDict`` object has all the specified
1066-
keys. This will be flagged as an error:
1067-
1068-
.. code-block:: python
1069-
1070-
# Error: 'year' missing
1071-
toy_story: Movie = {'name': 'Toy Story'}
1072-
1073-
Sometimes you want to allow keys to be left out when creating a
1074-
``TypedDict`` object. You can provide the ``total=False`` argument to
1075-
``TypedDict(...)`` to achieve this:
1076-
1077-
.. code-block:: python
1078-
1079-
GuiOptions = TypedDict(
1080-
'GuiOptions', {'language': str, 'color': str}, total=False)
1081-
options: GuiOptions = {} # Okay
1082-
options['language'] = 'en'
1083-
1084-
You may need to use :py:meth:`~dict.get` to access items of a partial (non-total)
1085-
``TypedDict``, since indexing using ``[]`` could fail at runtime.
1086-
However, mypy still lets use ``[]`` with a partial ``TypedDict`` -- you
1087-
just need to be careful with it, as it could result in a :py:exc:`KeyError`.
1088-
Requiring :py:meth:`~dict.get` everywhere would be too cumbersome. (Note that you
1089-
are free to use :py:meth:`~dict.get` with total ``TypedDict``\s as well.)
1090-
1091-
Keys that aren't required are shown with a ``?`` in error messages:
1092-
1093-
.. code-block:: python
1094-
1095-
# Revealed type is "TypedDict('GuiOptions', {'language'?: builtins.str,
1096-
# 'color'?: builtins.str})"
1097-
reveal_type(options)
1098-
1099-
Totality also affects structural compatibility. You can't use a partial
1100-
``TypedDict`` when a total one is expected. Also, a total ``TypedDict`` is not
1101-
valid when a partial one is expected.
1102-
1103-
Supported operations
1104-
--------------------
1105-
1106-
``TypedDict`` objects support a subset of dictionary operations and methods.
1107-
You must use string literals as keys when calling most of the methods,
1108-
as otherwise mypy won't be able to check that the key is valid. List
1109-
of supported operations:
1110-
1111-
* Anything included in :py:class:`~typing.Mapping`:
1112-
1113-
* ``d[key]``
1114-
* ``key in d``
1115-
* ``len(d)``
1116-
* ``for key in d`` (iteration)
1117-
* :py:meth:`d.get(key[, default]) <dict.get>`
1118-
* :py:meth:`d.keys() <dict.keys>`
1119-
* :py:meth:`d.values() <dict.values>`
1120-
* :py:meth:`d.items() <dict.items>`
1121-
1122-
* :py:meth:`d.copy() <dict.copy>`
1123-
* :py:meth:`d.setdefault(key, default) <dict.setdefault>`
1124-
* :py:meth:`d1.update(d2) <dict.update>`
1125-
* :py:meth:`d.pop(key[, default]) <dict.pop>` (partial ``TypedDict``\s only)
1126-
* ``del d[key]`` (partial ``TypedDict``\s only)
1127-
1128-
.. note::
1129-
1130-
:py:meth:`~dict.clear` and :py:meth:`~dict.popitem` are not supported since they are unsafe
1131-
-- they could delete required ``TypedDict`` items that are not visible to
1132-
mypy because of structural subtyping.
1133-
1134-
Class-based syntax
1135-
------------------
1136-
1137-
An alternative, class-based syntax to define a ``TypedDict`` is supported
1138-
in Python 3.6 and later:
1139-
1140-
.. code-block:: python
1141-
1142-
from typing_extensions import TypedDict
1143-
1144-
class Movie(TypedDict):
1145-
name: str
1146-
year: int
1147-
1148-
The above definition is equivalent to the original ``Movie``
1149-
definition. It doesn't actually define a real class. This syntax also
1150-
supports a form of inheritance -- subclasses can define additional
1151-
items. However, this is primarily a notational shortcut. Since mypy
1152-
uses structural compatibility with ``TypedDict``\s, inheritance is not
1153-
required for compatibility. Here is an example of inheritance:
1154-
1155-
.. code-block:: python
1156-
1157-
class Movie(TypedDict):
1158-
name: str
1159-
year: int
1160-
1161-
class BookBasedMovie(Movie):
1162-
based_on: str
1163-
1164-
Now ``BookBasedMovie`` has keys ``name``, ``year`` and ``based_on``.
1165-
1166-
Mixing required and non-required items
1167-
--------------------------------------
1168-
1169-
In addition to allowing reuse across ``TypedDict`` types, inheritance also allows
1170-
you to mix required and non-required (using ``total=False``) items
1171-
in a single ``TypedDict``. Example:
1172-
1173-
.. code-block:: python
1174-
1175-
class MovieBase(TypedDict):
1176-
name: str
1177-
year: int
1178-
1179-
class Movie(MovieBase, total=False):
1180-
based_on: str
1181-
1182-
Now ``Movie`` has required keys ``name`` and ``year``, while ``based_on``
1183-
can be left out when constructing an object. A ``TypedDict`` with a mix of required
1184-
and non-required keys, such as ``Movie`` above, will only be compatible with
1185-
another ``TypedDict`` if all required keys in the other ``TypedDict`` are required keys in the
1186-
first ``TypedDict``, and all non-required keys of the other ``TypedDict`` are also non-required keys
1187-
in the first ``TypedDict``.
1188-
1189-
Unions of TypedDicts
1190-
--------------------
1191-
1192-
Since TypedDicts are really just regular dicts at runtime, it is not possible to
1193-
use ``isinstance`` checks to distinguish between different variants of a Union of
1194-
TypedDict in the same way you can with regular objects.
1195-
1196-
Instead, you can use the :ref:`tagged union pattern <tagged_unions>`. The referenced
1197-
section of the docs has a full description with an example, but in short, you will
1198-
need to give each TypedDict the same key where each value has a unique
1199-
:ref:`Literal type <literal_types>`. Then, check that key to distinguish
1200-
between your TypedDicts.

0 commit comments

Comments
 (0)