Skip to content

Commit

Permalink
[3.9] bpo-46677: Add examples of inheritance and attributes to `Typed…
Browse files Browse the repository at this point in the history
…Dict` docs. (GH-31349) (GH-31808)

* bpo-46677: Add examples of inheritance and attributes to `TypedDict` docs (GH-31349)

Co-authored-by: Jelle Zijlstra <[email protected]>
(cherry picked from commit 8a207e0)
  • Loading branch information
CharlieZhao95 authored Mar 12, 2022
1 parent 49ff5ed commit b5140a5
Showing 1 changed file with 106 additions and 12 deletions.
118 changes: 106 additions & 12 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1056,26 +1056,120 @@ These are not used in annotations. They are building blocks for declaring types.

assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')

The type info for introspection can be accessed via ``Point2D.__annotations__``
and ``Point2D.__total__``. To allow using this feature with older versions
of Python that do not support :pep:`526`, ``TypedDict`` supports two additional
equivalent syntactic forms::
To allow using this feature with older versions of Python that do not
support :pep:`526`, ``TypedDict`` supports two additional equivalent
syntactic forms:

* Using a literal :class:`dict` as the second argument::

Point2D = TypedDict('Point2D', x=int, y=int, label=str)
Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})

By default, all keys must be present in a TypedDict. It is possible
to override this by specifying totality.
* Using keyword arguments::

Point2D = TypedDict('Point2D', x=int, y=int, label=str)

The functional syntax should also be used when any of the keys are not valid
:ref:`identifiers`, for example because they are keywords or contain hyphens.
Example::

# raises SyntaxError
class Point2D(TypedDict):
in: int # 'in' is a keyword
x-y: int # name with hyphens

# OK, functional syntax
Point2D = TypedDict('Point2D', {'in': int, 'x-y': int})

By default, all keys must be present in a ``TypedDict``. It is possible to
override this by specifying totality.
Usage::

class point2D(TypedDict, total=False):
class Point2D(TypedDict, total=False):
x: int
y: int

This means that a point2D TypedDict can have any of the keys omitted. A type
checker is only expected to support a literal False or True as the value of
the total argument. True is the default, and makes all items defined in the
class body be required.
# Alternative syntax
Point2D = TypedDict('Point2D', {'x': int, 'y': int}, total=False)

This means that a ``Point2D`` ``TypedDict`` can have any of the keys
omitted. A type checker is only expected to support a literal ``False`` or
``True`` as the value of the ``total`` argument. ``True`` is the default,
and makes all items defined in the class body required.

It is possible for a ``TypedDict`` type to inherit from one or more other ``TypedDict`` types
using the class-based syntax.
Usage::

class Point3D(Point2D):
z: int

``Point3D`` has three items: ``x``, ``y`` and ``z``. It is equivalent to this
definition::

class Point3D(TypedDict):
x: int
y: int
z: int

A ``TypedDict`` cannot inherit from a non-TypedDict class,
notably including :class:`Generic`. For example::

class X(TypedDict):
x: int

class Y(TypedDict):
y: int

class Z(object): pass # A non-TypedDict class

class XY(X, Y): pass # OK

class XZ(X, Z): pass # raises TypeError

T = TypeVar('T')
class XT(X, Generic[T]): pass # raises TypeError

A ``TypedDict`` can be introspected via :attr:`__annotations__`,
:attr:`__total__`, :attr:`__required_keys__`, and :attr:`__optional_keys__`.

.. attribute:: __total__

``Point2D.__total__`` gives the value of the ``total`` argument.
Example::

>>> from typing import TypedDict
>>> class Point2D(TypedDict): pass
>>> Point2D.__total__
True
>>> class Point2D(TypedDict, total=False): pass
>>> Point2D.__total__
False
>>> class Point3D(Point2D): pass
>>> Point3D.__total__
True

.. attribute:: __required_keys__
.. attribute:: __optional_keys__

``Point2D.__required_keys__`` and ``Point2D.__optional_keys__`` return
:class:`frozenset` objects containing required and non-required keys, respectively.
Currently the only way to declare both required and non-required keys in the
same ``TypedDict`` is mixed inheritance, declaring a ``TypedDict`` with one value
for the ``total`` argument and then inheriting it from another ``TypedDict`` with
a different value for ``total``.
Usage::

>>> class Point2D(TypedDict, total=False):
... x: int
... y: int
...
>>> class Point3D(Point2D):
... z: int
...
>>> Point3D.__required_keys__ == frozenset({'z'})
True
>>> Point3D.__optional_keys__ == frozenset({'x', 'y'})
True

See :pep:`589` for more examples and detailed rules of using ``TypedDict``.

Expand Down

0 comments on commit b5140a5

Please sign in to comment.