Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-46677: Add examples of inheritance and attributes to TypedDict docs. #31349

Merged
merged 15 commits into from
Mar 10, 2022
Merged
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 88 additions & 3 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1465,9 +1465,6 @@ 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__``,
``Point2D.__total__``, ``Point2D.__required_keys__``, and
``Point2D.__optional_keys__``.
To allow using this feature with older versions of Python that do not
support :pep:`526`, ``TypedDict`` supports two additional equivalent
syntactic forms:
Expand All @@ -1484,6 +1481,18 @@ These are not used in annotations. They are building blocks for declaring types.
The keyword-argument syntax is deprecated in 3.11 and will be removed
in 3.13. It may also be unsupported by static type checkers.

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.
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
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::
Expand All @@ -1500,6 +1509,82 @@ These are not used in annotations. They are building blocks for declaring types.
``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

The type info for introspection can be accessed via annotations dicts
CharlieZhao95 marked this conversation as resolved.
Show resolved Hide resolved
(see :ref:`annotations-howto` for more information on annotations best practices),
: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``.

.. versionadded:: 3.8
Expand Down