gh-103921: Document PEP 695 (#104642)

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Jelle Zijlstra 2023-05-26 10:48:17 -07:00 committed by GitHub
parent 95f1b1fef7
commit 060277d96b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1240 additions and 308 deletions

View File

@ -917,6 +917,25 @@ Statements
type_ignores=[])
.. class:: TypeAlias(name, type_params, value)
A :ref:`type alias <type-aliases>` created through the :keyword:`type`
statement. ``name`` is the name of the alias, ``type_params`` is a list of
:ref:`type parameters <ast-type-params>`, and ``value`` is the value of the
type alias.
.. doctest::
>>> print(ast.dump(ast.parse('type Alias = int'), indent=4))
Module(
body=[
TypeAlias(
name=Name(id='Alias', ctx=Store()),
type_params=[],
value=Name(id='int', ctx=Load()))],
type_ignores=[])
Other statements which are only applicable inside functions or loops are
described in other sections.
@ -1644,15 +1663,93 @@ Pattern matching
value=Constant(value=Ellipsis))])])],
type_ignores=[])
.. _ast-type-params:
Type parameters
^^^^^^^^^^^^^^^
:ref:`Type parameters <type-params>` can exist on classes, functions, and type
aliases.
.. class:: TypeVar(name, bound)
A :class:`typing.TypeVar`. ``name`` is the name of the type variable.
``bound`` is the bound or constraints, if any. If ``bound`` is a :class:`Tuple`,
it represents constraints; otherwise it represents the bound.
.. doctest::
>>> print(ast.dump(ast.parse("type Alias[T: int] = list[T]"), indent=4))
Module(
body=[
TypeAlias(
name=Name(id='Alias', ctx=Store()),
type_params=[
TypeVar(
name='T',
bound=Name(id='int', ctx=Load()))],
value=Subscript(
value=Name(id='list', ctx=Load()),
slice=Name(id='T', ctx=Load()),
ctx=Load()))],
type_ignores=[])
.. class:: ParamSpec(name)
A :class:`typing.ParamSpec`. ``name`` is the name of the parameter specification.
.. doctest::
>>> print(ast.dump(ast.parse("type Alias[**P] = Callable[P, int]"), indent=4))
Module(
body=[
TypeAlias(
name=Name(id='Alias', ctx=Store()),
type_params=[
ParamSpec(name='P')],
value=Subscript(
value=Name(id='Callable', ctx=Load()),
slice=Tuple(
elts=[
Name(id='P', ctx=Load()),
Name(id='int', ctx=Load())],
ctx=Load()),
ctx=Load()))],
type_ignores=[])
.. class:: TypeVarTuple(name)
A :class:`typing.TypeVarTuple`. ``name`` is the name of the type variable tuple.
.. doctest::
>>> print(ast.dump(ast.parse("type Alias[*Ts] = tuple[*Ts]"), indent=4))
Module(
body=[
TypeAlias(
name=Name(id='Alias', ctx=Store()),
type_params=[
TypeVarTuple(name='Ts')],
value=Subscript(
value=Name(id='tuple', ctx=Load()),
slice=Tuple(
elts=[
Starred(
value=Name(id='Ts', ctx=Load()),
ctx=Load())],
ctx=Load()),
ctx=Load()))],
type_ignores=[])
Function and class definitions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. class:: FunctionDef(name, args, body, decorator_list, returns, type_comment)
.. class:: FunctionDef(name, type_params, args, body, decorator_list, returns, type_comment)
A function definition.
* ``name`` is a raw string of the function name.
* ``type_params`` is a list of :ref:`type parameters <ast-type-params>`.
* ``args`` is an :class:`arguments` node.
* ``body`` is the list of nodes inside the function.
* ``decorator_list`` is the list of decorators to be applied, stored outermost
@ -1820,11 +1917,12 @@ Function and class definitions
type_ignores=[])
.. class:: ClassDef(name, bases, keywords, body, decorator_list)
.. class:: ClassDef(name, type_params, bases, keywords, body, decorator_list)
A class definition.
* ``name`` is a raw string for the class name
* ``type_params`` is a list of :ref:`type parameters <ast-type-params>`.
* ``bases`` is a list of nodes for explicitly specified base classes.
* ``keywords`` is a list of :class:`keyword` nodes, principally for 'metaclass'.
Other keywords will be passed to the metaclass, as per `PEP-3115

View File

@ -188,9 +188,9 @@ operation is being performed, so the intermediate analysis object isn't useful:
For a module, it disassembles all functions. For a class, it disassembles
all methods (including class and static methods). For a code object or
sequence of raw bytecode, it prints one line per bytecode instruction.
It also recursively disassembles nested code objects (the code of
comprehensions, generator expressions and nested functions, and the code
used for building nested classes).
It also recursively disassembles nested code objects. These can include
generator expressions, nested functions, the bodies of nested classes,
and the code objects used for :ref:`annotation scopes <annotation-scopes>`.
Strings are first compiled to code objects with the :func:`compile`
built-in function before being disassembled. If no object is provided, this
function disassembles the last traceback.
@ -926,6 +926,27 @@ iterations of the loop.
.. opcode:: LOAD_NAME (namei)
Pushes the value associated with ``co_names[namei]`` onto the stack.
The name is looked up within the locals, then the globals, then the builtins.
.. opcode:: LOAD_LOCALS
Pushes a reference to the locals dictionary onto the stack. This is used
to prepare namespace dictionaries for :opcode:`LOAD_FROM_DICT_OR_DEREF`
and :opcode:`LOAD_FROM_DICT_OR_GLOBALS`.
.. versionadded:: 3.12
.. opcode:: LOAD_FROM_DICT_OR_GLOBALS (i)
Pops a mapping off the stack and looks up the value for ``co_names[namei]``.
If the name is not found there, looks it up in the globals and then the builtins,
similar to :opcode:`LOAD_GLOBAL`.
This is used for loading global variables in
:ref:`annotation scopes <annotation-scopes>` within class bodies.
.. versionadded:: 3.12
.. opcode:: BUILD_TUPLE (count)
@ -1243,16 +1264,17 @@ iterations of the loop.
``i`` is no longer offset by the length of ``co_varnames``.
.. opcode:: LOAD_CLASSDEREF (i)
.. opcode:: LOAD_FROM_DICT_OR_DEREF (i)
Much like :opcode:`LOAD_DEREF` but first checks the locals dictionary before
consulting the cell. This is used for loading free variables in class
bodies.
Pops a mapping off the stack and looks up the name associated with
slot ``i`` of the "fast locals" storage in this mapping.
If the name is not found there, loads it from the cell contained in
slot ``i``, similar to :opcode:`LOAD_DEREF`. This is used for loading
free variables in class bodies (which previously used
:opcode:`!LOAD_CLASSDEREF`) and in
:ref:`annotation scopes <annotation-scopes>` within class bodies.
.. versionadded:: 3.4
.. versionchanged:: 3.11
``i`` is no longer offset by the length of ``co_varnames``.
.. versionadded:: 3.12
.. opcode:: STORE_DEREF (i)
@ -1504,13 +1526,45 @@ iterations of the loop.
The operand determines which intrinsic function is called:
* ``0`` Not valid
* ``1`` Prints the argument to standard out. Used in the REPL.
* ``2`` Performs ``import *`` for the named module.
* ``3`` Extracts the return value from a ``StopIteration`` exception.
* ``4`` Wraps an aync generator value
* ``5`` Performs the unary ``+`` operation
* ``6`` Converts a list to a tuple
+-----------------------------------+-----------------------------------+
| Operand | Description |
+===================================+===================================+
| ``INTRINSIC_1_INVALID`` | Not valid |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_PRINT`` | Prints the argument to standard |
| | out. Used in the REPL. |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_IMPORT_STAR`` | Performs ``import *`` for the |
| | named module. |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_STOPITERATION_ERROR`` | Extracts the return value from a |
| | ``StopIteration`` exception. |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_ASYNC_GEN_WRAP`` | Wraps an aync generator value |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_UNARY_POSITIVE`` | Performs the unary ``+`` |
| | operation |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_LIST_TO_TUPLE`` | Converts a list to a tuple |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_TYPEVAR`` | Creates a :class:`typing.TypeVar` |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_PARAMSPEC`` | Creates a |
| | :class:`typing.ParamSpec` |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_TYPEVARTUPLE`` | Creates a |
| | :class:`typing.TypeVarTuple` |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_SUBSCRIPT_GENERIC`` | Returns :class:`typing.Generic` |
| | subscripted with the argument |
+-----------------------------------+-----------------------------------+
| ``INTRINSIC_TYPEALIAS`` | Creates a |
| | :class:`typing.TypeAliasType`; |
| | used in the :keyword:`type` |
| | statement. The argument is a tuple|
| | of the type alias's name, |
| | type parameters, and value. |
+-----------------------------------+-----------------------------------+
.. versionadded:: 3.12
@ -1522,8 +1576,25 @@ iterations of the loop.
The operand determines which intrinsic function is called:
* ``0`` Not valid
* ``1`` Calculates the :exc:`ExceptionGroup` to raise from a ``try-except*``.
+----------------------------------------+-----------------------------------+
| Operand | Description |
+========================================+===================================+
| ``INTRINSIC_2_INVALID`` | Not valid |
+----------------------------------------+-----------------------------------+
| ``INTRINSIC_PREP_RERAISE_STAR`` | Calculates the |
| | :exc:`ExceptionGroup` to raise |
| | from a ``try-except*``. |
+----------------------------------------+-----------------------------------+
| ``INTRINSIC_TYPEVAR_WITH_BOUND`` | Creates a :class:`typing.TypeVar` |
| | with a bound. |
+----------------------------------------+-----------------------------------+
| ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` | Creates a |
| | :class:`typing.TypeVar` with |
| | constraints. |
+----------------------------------------+-----------------------------------+
| ``INTRINSIC_SET_FUNCTION_TYPE_PARAMS`` | Sets the ``__type_params__`` |
| | attribute of a function. |
+----------------------------------------+-----------------------------------+
.. versionadded:: 3.12

View File

@ -5476,6 +5476,14 @@ types, where they are relevant. Some of these are not reported by the
.. versionadded:: 3.3
.. attribute:: definition.__type_params__
The :ref:`type parameters <type-params>` of generic classes, functions,
and :ref:`type aliases <type-aliases>`.
.. versionadded:: 3.12
.. attribute:: class.__mro__
This attribute is a tuple of classes that are considered when looking for

View File

@ -101,6 +101,8 @@ annotations. These include:
* :pep:`692`: Using ``TypedDict`` for more precise ``**kwargs`` typing
*Introducing* a new way of typing ``**kwargs`` with :data:`Unpack` and
:data:`TypedDict`
* :pep:`695`: Type Parameter Syntax
*Introducing* builtin syntax for creating generic functions, classes, and type aliases.
* :pep:`698`: Adding an override decorator to typing
*Introducing* the :func:`@override<override>` decorator
@ -109,10 +111,12 @@ annotations. These include:
Type aliases
============
A type alias is defined by assigning the type to the alias. In this example,
``Vector`` and ``list[float]`` will be treated as interchangeable synonyms::
A type alias is defined using the :keyword:`type` statement, which creates
an instance of :class:`TypeAliasType`. In this example,
``Vector`` and ``list[float]`` will be treated equivalently by static type
checkers::
Vector = list[float]
type Vector = list[float]
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
@ -124,9 +128,9 @@ Type aliases are useful for simplifying complex type signatures. For example::
from collections.abc import Sequence
ConnectionOptions = dict[str, str]
Address = tuple[str, int]
Server = tuple[Address, ConnectionOptions]
type ConnectionOptions = dict[str, str]
type Address = tuple[str, int]
type Server = tuple[Address, ConnectionOptions]
def broadcast_message(message: str, servers: Sequence[Server]) -> None:
...
@ -141,6 +145,18 @@ Type aliases are useful for simplifying complex type signatures. For example::
Note that ``None`` as a type hint is a special case and is replaced by
``type(None)``.
The :keyword:`type` statement is new in Python 3.12. For backwards
compatibility, type aliases can also be created through simple assignment::
Vector = list[float]
Or marked with :data:`TypeAlias` to make it explicit that this is a type alias,
not a normal variable assignment::
from typing import TypeAlias
Vector: TypeAlias = list[float]
.. _distinct:
NewType
@ -206,7 +222,7 @@ See :pep:`484` for more details.
.. note::
Recall that the use of a type alias declares two types to be *equivalent* to
one another. Doing ``Alias = Original`` will make the static type checker
one another. Doing ``type Alias = Original`` will make the static type checker
treat ``Alias`` as being *exactly equivalent* to ``Original`` in all cases.
This is useful when you want to simplify complex type signatures.
@ -282,19 +298,26 @@ subscription to denote expected types for container elements.
def notify_by_email(employees: Sequence[Employee],
overrides: Mapping[str, str]) -> None: ...
Generics can be parameterized by using a factory available in typing
called :class:`TypeVar`.
Generics can be parameterized by using :ref:`type parameter syntax <type-params>`::
::
from collections.abc import Sequence
def first[T](l: Sequence[T]) -> T: # Generic function
return l[0]
Or by using the :class:`TypeVar` factory directly::
from collections.abc import Sequence
from typing import TypeVar
T = TypeVar('T') # Declare type variable
U = TypeVar('U') # Declare type variable
def first(l: Sequence[T]) -> T: # Generic function
def first(l: Sequence[U]) -> U: # Generic function
return l[0]
.. versionchanged:: 3.12
Syntactic support for generics is new in Python 3.12.
.. _user-defined-generics:
User-defined generic types
@ -304,12 +327,9 @@ A user-defined class can be defined as a generic class.
::
from typing import TypeVar, Generic
from logging import Logger
T = TypeVar('T')
class LoggedVar(Generic[T]):
class LoggedVar[T]:
def __init__(self, value: T, name: str, logger: Logger) -> None:
self.name = name
self.logger = logger
@ -326,12 +346,23 @@ A user-defined class can be defined as a generic class.
def log(self, message: str) -> None:
self.logger.info('%s: %s', self.name, message)
``Generic[T]`` as a base class defines that the class ``LoggedVar`` takes a
single type parameter ``T`` . This also makes ``T`` valid as a type within the
class body.
This syntax indicates that the class ``LoggedVar`` is parameterised around a
single :class:`type variable <TypeVar>` ``T`` . This also makes ``T`` valid as
a type within the class body.
The :class:`Generic` base class defines :meth:`~object.__class_getitem__` so
that ``LoggedVar[T]`` is valid as a type::
Generic classes implicitly inherit from :class:`Generic`. For compatibility
with Python 3.11 and lower, it is also possible to inherit explicitly from
:class:`Generic` to indicate a generic class::
from typing import TypeVar, Generic
T = TypeVar('T')
class LoggedVar(Generic[T]):
...
Generic classes have :meth:`~object.__class_getitem__` methods, meaning they
can be parameterised at runtime (e.g. ``LoggedVar[int]`` below)::
from collections.abc import Iterable
@ -344,11 +375,14 @@ A generic type can have any number of type variables. All varieties of
from typing import TypeVar, Generic, Sequence
T = TypeVar('T', contravariant=True)
B = TypeVar('B', bound=Sequence[bytes], covariant=True)
S = TypeVar('S', int, str)
class WeirdTrio[T, B: Sequence[bytes], S: (int, str)]:
...
class WeirdTrio(Generic[T, B, S]):
OldT = TypeVar('OldT', contravariant=True)
OldB = TypeVar('OldB', bound=Sequence[bytes], covariant=True)
OldS = TypeVar('OldS', int, str)
class OldWeirdTrio(Generic[OldT, OldB, OldS]):
...
Each type variable argument to :class:`Generic` must be distinct.
@ -357,29 +391,26 @@ This is thus invalid::
from typing import TypeVar, Generic
...
class Pair[M, M]: # SyntaxError
...
T = TypeVar('T')
class Pair(Generic[T, T]): # INVALID
...
You can use multiple inheritance with :class:`Generic`::
Generic classes can also inherit from other classes::
from collections.abc import Sized
from typing import TypeVar, Generic
T = TypeVar('T')
class LinkedList(Sized, Generic[T]):
class LinkedList[T](Sized):
...
When inheriting from generic classes, some type variables could be fixed::
When inheriting from generic classes, some type parameters could be fixed::
from collections.abc import Mapping
from typing import TypeVar
T = TypeVar('T')
class MyDict(Mapping[str, T]):
class MyDict[T](Mapping[str, T]):
...
In this case ``MyDict`` has a single parameter, ``T``.
@ -392,49 +423,66 @@ not generic but implicitly inherits from ``Iterable[Any]``::
class MyIterable(Iterable): # Same as Iterable[Any]
User defined generic type aliases are also supported. Examples::
User-defined generic type aliases are also supported. Examples::
from collections.abc import Iterable
from typing import TypeVar
S = TypeVar('S')
Response = Iterable[S] | int
type Response[S] = Iterable[S] | int
# Return type here is same as Iterable[str] | int
def response(query: str) -> Response[str]:
...
T = TypeVar('T', int, float, complex)
Vec = Iterable[tuple[T, T]]
type Vec[T] = Iterable[tuple[T, T]]
def inproduct(v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]]
def inproduct[T: (int, float, complex)](v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]]
return sum(x*y for x, y in v)
For backward compatibility, generic type aliases can also be created
through a simple assignment::
from collections.abc import Iterable
from typing import TypeVar
S = TypeVar("S")
Response = Iterable[S] | int
.. versionchanged:: 3.7
:class:`Generic` no longer has a custom metaclass.
.. versionchanged:: 3.12
Syntactic support for generics and type aliases is new in version 3.12.
Previously, generic classes had to explicitly inherit from :class:`Generic`
or contain a type variable in one of their bases.
User-defined generics for parameter expressions are also supported via parameter
specification variables in the form ``Generic[P]``. The behavior is consistent
specification variables in the form ``[**P]``. The behavior is consistent
with type variables' described above as parameter specification variables are
treated by the typing module as a specialized type variable. The one exception
to this is that a list of types can be used to substitute a :class:`ParamSpec`::
>>> from typing import Generic, ParamSpec, TypeVar
>>> T = TypeVar('T')
>>> P = ParamSpec('P')
>>> class Z(Generic[T, P]): ...
>>> class Z[T, **P]: ... # T is a TypeVar; P is a ParamSpec
...
>>> Z[int, [dict, float]]
__main__.Z[int, [dict, float]]
Classes generic over a :class:`ParamSpec` can also be created using explicit
inheritance from :class:`Generic`. In this case, ``**`` is not used::
Furthermore, a generic with only one parameter specification variable will accept
from typing import ParamSpec, Generic
P = ParamSpec('P')
class Z(Generic[P]):
...
Another difference between :class:`TypeVar` and :class:`ParamSpec` is that a
generic with only one parameter specification variable will accept
parameter lists in the forms ``X[[Type1, Type2, ...]]`` and also
``X[Type1, Type2, ...]`` for aesthetic reasons. Internally, the latter is converted
to the former, so the following are equivalent::
>>> class X(Generic[P]): ...
>>> class X[**P]: ...
...
>>> X[int, str]
__main__.X[[int, str]]
@ -670,20 +718,20 @@ These can be used as types in annotations and do not support ``[]``.
This can be used to define a function that should never be
called, or a function that never returns::
from typing import Never
from typing import Never
def never_call_me(arg: Never) -> None:
pass
def never_call_me(arg: Never) -> None:
pass
def int_or_str(arg: int | str) -> None:
never_call_me(arg) # type checker error
match arg:
case int():
print("It's an int")
case str():
print("It's a str")
case _:
never_call_me(arg) # ok, arg is of type Never
def int_or_str(arg: int | str) -> None:
never_call_me(arg) # type checker error
match arg:
case int():
print("It's an int")
case str():
print("It's a str")
case _:
never_call_me(arg) # ok, arg is of type Never
.. versionadded:: 3.11
@ -717,9 +765,9 @@ These can be used as types in annotations and do not support ``[]``.
from typing import Self
class Foo:
def return_self(self) -> Self:
...
return self
def return_self(self) -> Self:
...
return self
This annotation is semantically equivalent to the following,
@ -730,16 +778,16 @@ These can be used as types in annotations and do not support ``[]``.
Self = TypeVar("Self", bound="Foo")
class Foo:
def return_self(self: Self) -> Self:
...
return self
def return_self(self: Self) -> Self:
...
return self
In general if something currently follows the pattern of::
class Foo:
def return_self(self) -> "Foo":
...
return self
def return_self(self) -> "Foo":
...
return self
You should use :data:`Self` as calls to ``SubclassOfFoo.return_self`` would have
``Foo`` as the return type and not ``SubclassOfFoo``.
@ -767,6 +815,15 @@ These can be used as types in annotations and do not support ``[]``.
.. versionadded:: 3.10
.. deprecated:: 3.12
:data:`TypeAlias` is deprecated in favor of the :keyword:`type` statement,
which creates instances of :class:`TypeAliasType`.
Note that while :data:`TypeAlias` and :class:`TypeAliasType` serve
similar purposes and have similar names, they are distinct and the
latter is not the type of the former.
Removal of :data:`TypeAlias` is not currently planned, but users
are encouraged to migrate to :keyword:`type` statements.
Special forms
"""""""""""""
@ -1255,8 +1312,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn
from typing import TypedDict, Unpack
class Movie(TypedDict):
name: str
year: int
name: str
year: int
# This function expects two keyword arguments - `name` of type `str`
# and `year` of type `int`.
@ -1266,213 +1323,327 @@ These can be used as types in annotations using ``[]``, each having a unique syn
.. versionadded:: 3.11
Building generic types
""""""""""""""""""""""
Building generic types and type aliases
"""""""""""""""""""""""""""""""""""""""
These are not used in annotations. They are building blocks for creating generic types.
The following objects are not used directly in annotations. Instead, they are building blocks
for creating generic types and type aliases.
These objects can be created through special syntax
(:ref:`type parameter lists <type-params>` and the :keyword:`type` statement).
For compatibility with Python 3.11 and earlier, they can also be created
without the dedicated syntax, as documented below.
.. class:: Generic
Abstract base class for generic types.
A generic type is typically declared by inheriting from an
instantiation of this class with one or more type variables.
For example, a generic mapping type might be defined as::
A generic type is typically declared by adding a list of type parameters
after the class name::
class Mapping[KT, VT]:
def __getitem__(self, key: KT) -> VT:
...
# Etc.
Such a class implicitly inherits from ``Generic``.
The runtime semantics of this syntax are discussed in the
:ref:`Language Reference <generic-classes>`.
This class can then be used as follows::
def lookup_name[X, Y](mapping: Mapping[X, Y], key: X, default: Y) -> Y:
try:
return mapping[key]
except KeyError:
return default
Here the brackets after the function name indicate a
:ref:`generic function <generic-functions>`.
For backwards compatibility, generic classes can also be
declared by explicitly inheriting from
``Generic``. In this case, the type parameters must be declared
separately::
KT = TypeVar('KT')
VT = TypeVar('VT')
class Mapping(Generic[KT, VT]):
def __getitem__(self, key: KT) -> VT:
...
# Etc.
This class can then be used as follows::
.. class:: TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False, infer_variance=False)
X = TypeVar('X')
Y = TypeVar('Y')
Type variable.
def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y:
try:
return mapping[key]
except KeyError:
return default
The preferred way to construct a type variable is via the dedicated syntax
for :ref:`generic functions <generic-functions>`,
:ref:`generic classes <generic-classes>`, and
:ref:`generic type aliases <generic-type-aliases>`::
.. class:: TypeVar
class Sequence[T]: # T is a TypeVar
...
Type variable.
This syntax can also be used to create bound and constrained type
variables::
Usage::
class StrSequence[S: str]: # S is a TypeVar bound to str
...
class StrOrBytesSequence[A: (str, bytes)]: # A is a TypeVar constrained to str or bytes
...
However, if desired, reusable type variables can also be constructed manually, like so::
T = TypeVar('T') # Can be anything
S = TypeVar('S', bound=str) # Can be any subtype of str
A = TypeVar('A', str, bytes) # Must be exactly str or bytes
Type variables exist primarily for the benefit of static type
checkers. They serve as the parameters for generic types as well
as for generic function definitions. See :class:`Generic` for more
information on generic types. Generic functions work as follows::
Type variables exist primarily for the benefit of static type
checkers. They serve as the parameters for generic types as well
as for generic function and type alias definitions.
See :class:`Generic` for more
information on generic types. Generic functions work as follows::
def repeat(x: T, n: int) -> Sequence[T]:
"""Return a list containing n references to x."""
return [x]*n
def repeat[T](x: T, n: int) -> Sequence[T]:
"""Return a list containing n references to x."""
return [x]*n
def print_capitalized(x: S) -> S:
"""Print x capitalized, and return x."""
print(x.capitalize())
return x
def print_capitalized[S: str](x: S) -> S:
"""Print x capitalized, and return x."""
print(x.capitalize())
return x
def concatenate(x: A, y: A) -> A:
"""Add two strings or bytes objects together."""
return x + y
def concatenate[A: (str, bytes)](x: A, y: A) -> A:
"""Add two strings or bytes objects together."""
return x + y
Note that type variables can be *bound*, *constrained*, or neither, but
cannot be both bound *and* constrained.
Note that type variables can be *bound*, *constrained*, or neither, but
cannot be both bound *and* constrained.
Bound type variables and constrained type variables have different
semantics in several important ways. Using a *bound* type variable means
that the ``TypeVar`` will be solved using the most specific type possible::
The variance of type variables is inferred by type checkers when they are created
through the :ref:`type parameter syntax <type-params>` or when
``infer_variance=True`` is passed.
Manually created type variables may be explicitly marked covariant or contravariant by passing
``covariant=True`` or ``contravariant=True``.
By default, manually created type variables are invariant.
See :pep:`484` and :pep:`695` for more details.
x = print_capitalized('a string')
reveal_type(x) # revealed type is str
Bound type variables and constrained type variables have different
semantics in several important ways. Using a *bound* type variable means
that the ``TypeVar`` will be solved using the most specific type possible::
class StringSubclass(str):
pass
x = print_capitalized('a string')
reveal_type(x) # revealed type is str
y = print_capitalized(StringSubclass('another string'))
reveal_type(y) # revealed type is StringSubclass
class StringSubclass(str):
pass
z = print_capitalized(45) # error: int is not a subtype of str
y = print_capitalized(StringSubclass('another string'))
reveal_type(y) # revealed type is StringSubclass
Type variables can be bound to concrete types, abstract types (ABCs or
protocols), and even unions of types::
z = print_capitalized(45) # error: int is not a subtype of str
U = TypeVar('U', bound=str|bytes) # Can be any subtype of the union str|bytes
V = TypeVar('V', bound=SupportsAbs) # Can be anything with an __abs__ method
Type variables can be bound to concrete types, abstract types (ABCs or
protocols), and even unions of types::
.. _typing-constrained-typevar:
# Can be anything with an __abs__ method
def print_abs[T: SupportsAbs](arg: T) -> None:
print("Absolute value:", abs(arg))
Using a *constrained* type variable, however, means that the ``TypeVar``
can only ever be solved as being exactly one of the constraints given::
U = TypeVar('U', bound=str|bytes) # Can be any subtype of the union str|bytes
V = TypeVar('V', bound=SupportsAbs) # Can be anything with an __abs__ method
a = concatenate('one', 'two')
reveal_type(a) # revealed type is str
.. _typing-constrained-typevar:
b = concatenate(StringSubclass('one'), StringSubclass('two'))
reveal_type(b) # revealed type is str, despite StringSubclass being passed in
Using a *constrained* type variable, however, means that the ``TypeVar``
can only ever be solved as being exactly one of the constraints given::
c = concatenate('one', b'two') # error: type variable 'A' can be either str or bytes in a function call, but not both
a = concatenate('one', 'two')
reveal_type(a) # revealed type is str
At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`. In general,
:func:`isinstance` and :func:`issubclass` should not be used with types.
b = concatenate(StringSubclass('one'), StringSubclass('two'))
reveal_type(b) # revealed type is str, despite StringSubclass being passed in
Type variables may be marked covariant or contravariant by passing
``covariant=True`` or ``contravariant=True``. See :pep:`484` for more
details. By default, type variables are invariant.
c = concatenate('one', b'two') # error: type variable 'A' can be either str or bytes in a function call, but not both
.. class:: TypeVarTuple
At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`.
Type variable tuple. A specialized form of :class:`type variable <TypeVar>`
that enables *variadic* generics.
.. attribute:: __name__
A normal type variable enables parameterization with a single type. A type
variable tuple, in contrast, allows parameterization with an
*arbitrary* number of types by acting like an *arbitrary* number of type
variables wrapped in a tuple. For example::
The name of the type variable.
T = TypeVar('T')
Ts = TypeVarTuple('Ts')
.. attribute:: __covariant__
def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
return (*tup[1:], tup[0])
Whether the type var has been explicitly marked as covariant.
# T is bound to int, Ts is bound to ()
# Return value is (1,), which has type tuple[int]
move_first_element_to_last(tup=(1,))
.. attribute:: __contravariant__
# T is bound to int, Ts is bound to (str,)
# Return value is ('spam', 1), which has type tuple[str, int]
move_first_element_to_last(tup=(1, 'spam'))
Whether the type var has been explicitly marked as contravariant.
# T is bound to int, Ts is bound to (str, float)
# Return value is ('spam', 3.0, 1), which has type tuple[str, float, int]
move_first_element_to_last(tup=(1, 'spam', 3.0))
.. attribute:: __infer_variance__
# This fails to type check (and fails at runtime)
# because tuple[()] is not compatible with tuple[T, *Ts]
# (at least one element is required)
move_first_element_to_last(tup=())
Whether the type variable's variance should be inferred by type checkers.
Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``.
Conceptually, you can think of ``Ts`` as a tuple of type variables
``(T1, T2, ...)``. ``tuple[T, *Ts]`` would then become
``tuple[T, *(T1, T2, ...)]``, which is equivalent to
``tuple[T, T1, T2, ...]``. (Note that in older versions of Python, you might
see this written using :data:`Unpack <Unpack>` instead, as
``Unpack[Ts]``.)
.. versionadded:: 3.12
Type variable tuples must *always* be unpacked. This helps distinguish type
variable tuples from normal type variables::
.. attribute:: __bound__
x: Ts # Not valid
x: tuple[Ts] # Not valid
x: tuple[*Ts] # The correct way to do it
The bound of the type variable, if any.
Type variable tuples can be used in the same contexts as normal type
variables. For example, in class definitions, arguments, and return types::
.. versionchanged:: 3.12
Shape = TypeVarTuple('Shape')
class Array(Generic[*Shape]):
def __getitem__(self, key: tuple[*Shape]) -> float: ...
def __abs__(self) -> "Array[*Shape]": ...
def get_shape(self) -> tuple[*Shape]: ...
For type variables created through :ref:`type parameter syntax <type-params>`,
the bound is evaluated only when the attribute is accessed, not when
the type variable is created (see :ref:`lazy-evaluation`).
Type variable tuples can be happily combined with normal type variables::
.. attribute:: __constraints__
DType = TypeVar('DType')
A tuple containing the constraints of the type variable, if any.
class Array(Generic[DType, *Shape]): # This is fine
pass
.. versionchanged:: 3.12
class Array2(Generic[*Shape, DType]): # This would also be fine
pass
For type variables created through :ref:`type parameter syntax <type-params>`,
the constraints are evaluated only when the attribute is accessed, not when
the type variable is created (see :ref:`lazy-evaluation`).
float_array_1d: Array[float, Height] = Array() # Totally fine
int_array_2d: Array[int, Height, Width] = Array() # Yup, fine too
.. versionchanged:: 3.12
However, note that at most one type variable tuple may appear in a single
list of type arguments or type parameters::
Type variables can now be declared using the
:ref:`type parameter <type-params>` syntax introduced by :pep:`695`.
The ``infer_variance`` parameter was added.
x: tuple[*Ts, *Ts] # Not valid
class Array(Generic[*Shape, *Shape]): # Not valid
pass
.. class:: TypeVarTuple(name)
Finally, an unpacked type variable tuple can be used as the type annotation
of ``*args``::
Type variable tuple. A specialized form of :class:`type variable <TypeVar>`
that enables *variadic* generics.
def call_soon(
callback: Callable[[*Ts], None],
*args: *Ts
) -> None:
...
callback(*args)
Type variable tuples can be declared in :ref:`type parameter lists <type-params>`
using a single asterisk (``*``) before the name::
In contrast to non-unpacked annotations of ``*args`` - e.g. ``*args: int``,
which would specify that *all* arguments are ``int`` - ``*args: *Ts``
enables reference to the types of the *individual* arguments in ``*args``.
Here, this allows us to ensure the types of the ``*args`` passed
to ``call_soon`` match the types of the (positional) arguments of
``callback``.
def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
return (*tup[1:], tup[0])
See :pep:`646` for more details on type variable tuples.
Or by explicitly invoking the ``TypeVarTuple`` constructor::
.. versionadded:: 3.11
T = TypeVar("T")
Ts = TypeVarTuple("Ts")
def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
return (*tup[1:], tup[0])
A normal type variable enables parameterization with a single type. A type
variable tuple, in contrast, allows parameterization with an
*arbitrary* number of types by acting like an *arbitrary* number of type
variables wrapped in a tuple. For example::
# T is bound to int, Ts is bound to ()
# Return value is (1,), which has type tuple[int]
move_first_element_to_last(tup=(1,))
# T is bound to int, Ts is bound to (str,)
# Return value is ('spam', 1), which has type tuple[str, int]
move_first_element_to_last(tup=(1, 'spam'))
# T is bound to int, Ts is bound to (str, float)
# Return value is ('spam', 3.0, 1), which has type tuple[str, float, int]
move_first_element_to_last(tup=(1, 'spam', 3.0))
# This fails to type check (and fails at runtime)
# because tuple[()] is not compatible with tuple[T, *Ts]
# (at least one element is required)
move_first_element_to_last(tup=())
Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``.
Conceptually, you can think of ``Ts`` as a tuple of type variables
``(T1, T2, ...)``. ``tuple[T, *Ts]`` would then become
``tuple[T, *(T1, T2, ...)]``, which is equivalent to
``tuple[T, T1, T2, ...]``. (Note that in older versions of Python, you might
see this written using :data:`Unpack <Unpack>` instead, as
``Unpack[Ts]``.)
Type variable tuples must *always* be unpacked. This helps distinguish type
variable tuples from normal type variables::
x: Ts # Not valid
x: tuple[Ts] # Not valid
x: tuple[*Ts] # The correct way to do it
Type variable tuples can be used in the same contexts as normal type
variables. For example, in class definitions, arguments, and return types::
class Array[*Shape]:
def __getitem__(self, key: tuple[*Shape]) -> float: ...
def __abs__(self) -> "Array[*Shape]": ...
def get_shape(self) -> tuple[*Shape]: ...
Type variable tuples can be happily combined with normal type variables::
DType = TypeVar('DType')
class Array[DType, *Shape]: # This is fine
pass
class Array2[*Shape, DType]: # This would also be fine
pass
float_array_1d: Array[float, Height] = Array() # Totally fine
int_array_2d: Array[int, Height, Width] = Array() # Yup, fine too
However, note that at most one type variable tuple may appear in a single
list of type arguments or type parameters::
x: tuple[*Ts, *Ts] # Not valid
class Array[*Shape, *Shape]: # Not valid
pass
Finally, an unpacked type variable tuple can be used as the type annotation
of ``*args``::
def call_soon[*Ts](
callback: Callable[[*Ts], None],
*args: *Ts
) -> None:
...
callback(*args)
In contrast to non-unpacked annotations of ``*args`` - e.g. ``*args: int``,
which would specify that *all* arguments are ``int`` - ``*args: *Ts``
enables reference to the types of the *individual* arguments in ``*args``.
Here, this allows us to ensure the types of the ``*args`` passed
to ``call_soon`` match the types of the (positional) arguments of
``callback``.
See :pep:`646` for more details on type variable tuples.
.. attribute:: __name__
The name of the type variable tuple.
.. versionadded:: 3.11
.. versionchanged:: 3.12
Type variable tuples can now be declared using the
:ref:`type parameter <type-params>` syntax introduced by :pep:`695`.
.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False)
Parameter specification variable. A specialized version of
:class:`type variables <TypeVar>`.
Usage::
In :ref:`type parameter lists <type-params>`, parameter specifications
can be declared with two asterisks (``**``)::
type IntFunc[**P] = Callable[P, int]
For compatibility with Python 3.11 and earlier, ``ParamSpec`` objects
can also be created as follows::
P = ParamSpec('P')
@ -1489,13 +1660,9 @@ These are not used in annotations. They are building blocks for creating generic
new callable returned by it have inter-dependent type parameters::
from collections.abc import Callable
from typing import TypeVar, ParamSpec
import logging
T = TypeVar('T')
P = ParamSpec('P')
def add_logging(f: Callable[P, T]) -> Callable[P, T]:
def add_logging[T, **P](f: Callable[P, T]) -> Callable[P, T]:
'''A type-safe decorator to add logging to a function.'''
def inner(*args: P.args, **kwargs: P.kwargs) -> T:
logging.info(f'{f.__name__} was called')
@ -1530,6 +1697,10 @@ These are not used in annotations. They are building blocks for creating generic
``P.args`` and ``P.kwargs`` are instances respectively of
:class:`ParamSpecArgs` and :class:`ParamSpecKwargs`.
.. attribute:: __name__
The name of the parameter specification.
Parameter specification variables created with ``covariant=True`` or
``contravariant=True`` can be used to declare covariant or contravariant
generic types. The ``bound`` argument is also accepted, similar to
@ -1538,6 +1709,11 @@ These are not used in annotations. They are building blocks for creating generic
.. versionadded:: 3.10
.. versionchanged:: 3.12
Parameter specifications can now be declared using the
:ref:`type parameter <type-params>` syntax introduced by :pep:`695`.
.. note::
Only parameter specification variables defined in global scope can
be pickled.
@ -1565,6 +1741,67 @@ These are not used in annotations. They are building blocks for creating generic
.. versionadded:: 3.10
.. class:: TypeAliasType(name, value, *, type_params=())
The type of type aliases created through the :keyword:`type` statement.
Example::
>>> type Alias = int
>>> type(Alias)
<class 'typing.TypeAliasType'>
.. versionadded:: 3.12
.. attribute:: __name__
The name of the type alias::
>>> type Alias = int
>>> Alias.__name__
'Alias'
.. attribute:: __module__
The module in which the type alias was defined::
>>> type Alias = int
>>> Alias.__module__
'__main__'
.. attribute:: __type_params__
The type parameters of the type alias, or an empty tuple if the alias is
not generic:
.. doctest::
>>> type ListOrSet[T] = list[T] | set[T]
>>> ListOrSet.__type_params__
(T,)
>>> type NotGeneric = int
>>> NotGeneric.__type_params__
()
.. attribute:: __value__
The type alias's value. This is :ref:`lazily evaluated <lazy-evaluation>`,
so names used in the definition of the alias are not resolved until the
``__value__`` attribute is accessed:
.. doctest::
>>> type Mutually = Recursive
>>> type Recursive = Mutually
>>> Mutually
Mutually
>>> Recursive
Recursive
>>> Mutually.__value__
Recursive
>>> Recursive.__value__
Mutually
Other special directives
""""""""""""""""""""""""
@ -1613,12 +1850,18 @@ These are not used in annotations. They are building blocks for declaring types.
``NamedTuple`` subclasses can be generic::
class Group(NamedTuple, Generic[T]):
class Group[T](NamedTuple):
key: T
group: list[T]
Backward-compatible usage::
# For creating a generic NamedTuple on Python 3.11 or lower
class Group(NamedTuple, Generic[T]):
key: T
group: list[T]
# A functional syntax is also supported
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
.. versionchanged:: 3.6
@ -1692,6 +1935,15 @@ These are not used in annotations. They are building blocks for declaring types.
Protocol classes can be generic, for example::
class GenProto[T](Protocol):
def meth(self) -> T:
...
In code that needs to be compatible with Python 3.11 or older, generic
Protocols can be written as follows::
T = TypeVar("T")
class GenProto(Protocol[T]):
def meth(self) -> T:
...
@ -1876,6 +2128,13 @@ These are not used in annotations. They are building blocks for declaring types.
A ``TypedDict`` can be generic::
class Group[T](TypedDict):
key: T
group: list[T]
To create a generic ``TypedDict`` that is compatible with Python 3.11
or lower, inherit from :class:`Generic` explicitly::
class Group(TypedDict, Generic[T]):
key: T
group: list[T]
@ -1977,12 +2236,10 @@ Corresponding to built-in types
This type may be used as follows::
T = TypeVar('T', int, float)
def vec2(x: T, y: T) -> List[T]:
def vec2[T: (int, float)](x: T, y: T) -> List[T]:
return [x, y]
def keep_positives(vector: Sequence[T]) -> List[T]:
def keep_positives[T: (int, float)](vector: Sequence[T]) -> List[T]:
return [item for item in vector if item > 0]
.. deprecated:: 3.9
@ -2173,8 +2430,8 @@ Corresponding to collections in :mod:`collections.abc`
A generic version of :class:`collections.abc.Mapping`.
This type can be used as follows::
def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
return word_list[word]
def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
return word_list[word]
.. deprecated:: 3.9
:class:`collections.abc.Mapping` now supports subscripting (``[]``).
@ -2987,3 +3244,5 @@ convenience. This is subject to change, and not all deprecations are listed.
| ``typing.Hashable`` and | 3.12 | Undecided | :gh:`94309` |
| ``typing.Sized`` | | | |
+----------------------------------+---------------+-------------------+----------------+
| ``typing.TypeAlias`` | 3.12 | Undecided | :pep:`695` |
+----------------------------------+---------------+-------------------+----------------+

View File

@ -1206,7 +1206,7 @@ A function definition defines a user-defined function object (see section
:ref:`types`):
.. productionlist:: python-grammar
funcdef: [`decorators`] "def" `funcname` "(" [`parameter_list`] ")"
funcdef: [`decorators`] "def" `funcname` [`type_params`] "(" [`parameter_list`] ")"
: ["->" `expression`] ":" `suite`
decorators: `decorator`+
decorator: "@" `assignment_expression` NEWLINE
@ -1256,6 +1256,15 @@ except that the original function is not temporarily bound to the name ``func``.
:token:`~python-grammar:assignment_expression`. Previously, the grammar was
much more restrictive; see :pep:`614` for details.
A list of :ref:`type parameters <type-params>` may be given in square brackets
between the function's name and the opening parenthesis for its parameter list.
This indicates to static type checkers that the function is generic. At runtime,
the type parameters can be retrieved from the function's ``__type_params__``
attribute. See :ref:`generic-functions` for more.
.. versionchanged:: 3.12
Type parameter lists are new in Python 3.12.
.. index::
triple: default; parameter; value
single: argument; function definition
@ -1378,7 +1387,7 @@ Class definitions
A class definition defines a class object (see section :ref:`types`):
.. productionlist:: python-grammar
classdef: [`decorators`] "class" `classname` [`inheritance`] ":" `suite`
classdef: [`decorators`] "class" `classname` [`type_params`] [`inheritance`] ":" `suite`
inheritance: "(" [`argument_list`] ")"
classname: `identifier`
@ -1434,6 +1443,15 @@ decorators. The result is then bound to the class name.
:token:`~python-grammar:assignment_expression`. Previously, the grammar was
much more restrictive; see :pep:`614` for details.
A list of :ref:`type parameters <type-params>` may be given in square brackets
immediately after the class's name.
This indicates to static type checkers that the class is generic. At runtime,
the type parameters can be retrieved from the class's ``__type_params__``
attribute. See :ref:`generic-classes` for more.
.. versionchanged:: 3.12
Type parameter lists are new in Python 3.12.
**Programmer's note:** Variables defined in the class definition are class
attributes; they are shared by instances. Instance attributes can be set in a
method with ``self.name = value``. Both class and instance attributes are
@ -1589,6 +1607,228 @@ body of a coroutine function.
The proposal that made coroutines a proper standalone concept in Python,
and added supporting syntax.
.. _type-params:
Type parameter lists
====================
.. versionadded:: 3.12
.. index::
single: type parameters
.. productionlist:: python-grammar
type_params: "[" `type_param` ("," `type_param`)* "]"
type_param: `typevar` | `typevartuple` | `paramspec`
typevar: `identifier` (":" `expression`)?
typevartuple: "*" `identifier`
paramspec: "**" `identifier`
:ref:`Functions <def>` (including :ref:`coroutines <async def>`),
:ref:`classes <class>` and :ref:`type aliases <type>` may
contain a type parameter list::
def max[T](args: list[T]) -> T:
...
async def amax[T](args: list[T]) -> T:
...
class Bag[T]:
def __iter__(self) -> Iterator[T]:
...
def add(self, arg: T) -> None:
...
type ListOrSet[T] = list[T] | set[T]
Semantically, this indicates that the function, class, or type alias is
generic over a type variable. This information is primarily used by static
type checkers, and at runtime, generic objects behave much like their
non-generic counterparts.
Type parameters are declared in square brackets (``[]``) immediately
after the name of the function, class, or type alias. The type parameters
are accessible within the scope of the generic object, but not elsewhere.
Thus, after a declaration ``def func[T](): pass``, the name ``T`` is not available in
the module scope. Below, the semantics of generic objects are described
with more precision. The scope of type parameters is modeled with a special
function (technically, an :ref:`annotation scope <annotation-scopes>`) that
wraps the creation of the generic object.
Generic functions, classes, and type aliases have a :attr:`!__type_params__`
attribute listing their type parameters.
Type parameters come in three kinds:
* :data:`typing.TypeVar`, introduced by a plain name (e.g., ``T``). Semantically, this
represents a single type to a type checker.
* :data:`typing.TypeVarTuple`, introduced by a name prefixed with a single
asterisk (e.g., ``*Ts``). Semantically, this stands for a tuple of any
number of types.
* :data:`typing.ParamSpec`, introduced by a name prefixed with two asterisks
(e.g., ``**P``). Semantically, this stands for the parameters of a callable.
:data:`typing.TypeVar` declarations can define *bounds* and *constraints* with
a colon (``:``) followed by an expression. A single expression after the colon
indicates a bound (e.g. ``T: int``). Semantically, this means
that the :data:`!typing.TypeVar` can only represent types that are a subtype of
this bound. A parenthesized tuple of expressions after the colon indicates a
set of constraints (e.g. ``T: (str, bytes)``). Each member of the tuple should be a
type (again, this is not enforced at runtime). Constrained type variables can only
take on one of the types in the list of constraints.
For :data:`!typing.TypeVar`\ s declared using the type parameter list syntax,
the bound and constraints are not evaluated when the generic object is created,
but only when the value is explicitly accessed through the attributes ``__bound__``
and ``__constraints__``. To accomplish this, the bounds or constraints are
evaluated in a separate :ref:`annotation scope <annotation-scopes>`.
:data:`typing.TypeVarTuple`\ s and :data:`typing.ParamSpec`\ s cannot have bounds
or constraints.
The following example indicates the full set of allowed type parameter declarations::
def overly_generic[
SimpleTypeVar,
TypeVarWithBound: int,
TypeVarWithConstraints: (str, bytes),
*SimpleTypeVarTuple,
**SimpleParamSpec,
](
a: SimpleTypeVar,
b: TypeVarWithBound,
c: Callable[SimpleParamSpec, TypeVarWithConstraints],
*d: SimpleTypeVarTuple,
): ...
.. _generic-functions:
Generic functions
-----------------
Generic functions are declared as follows::
def func[T](arg: T): ...
This syntax is equivalent to::
annotation-def TYPE_PARAMS_OF_func():
T = typing.TypeVar("T")
def func(arg: T): ...
func.__type_params__ = (T,)
return func
func = TYPE_PARAMS_OF_func()
Here ``annotation-def`` indicates an :ref:`annotation scope <annotation-scopes>`,
which is not actually bound to any name at runtime. (One
other liberty is taken in the translation: the syntax does not go through
attribute access on the :mod:`typing` module, but creates an instance of
:data:`typing.TypeVar` directly.)
The annotations of generic functions are evaluated within the annotation scope
used for declaring the type parameters, but the function's defaults and
decorators are not.
The following example illustrates the scoping rules for these cases,
as well as for additional flavors of type parameters::
@decorator
def func[T: int, *Ts, **P](*args: *Ts, arg: Callable[P, T] = some_default):
...
Except for the :ref:`lazy evaluation <lazy-evaluation>` of the
:class:`~typing.TypeVar` bound, this is equivalent to::
DEFAULT_OF_arg = some_default
annotation-def TYPE_PARAMS_OF_func():
annotation-def BOUND_OF_T():
return int
# In reality, BOUND_OF_T() is evaluated only on demand.
T = typing.TypeVar("T", bound=BOUND_OF_T())
Ts = typing.TypeVarTuple("Ts")
P = typing.ParamSpec("P")
def func(*args: *Ts, arg: Callable[P, T] = DEFAULT_OF_arg):
...
func.__type_params__ = (T, Ts, P)
return func
func = decorator(TYPE_PARAMS_OF_func())
The capitalized names like ``DEFAULT_OF_arg`` are not actually
bound at runtime.
.. _generic-classes:
Generic classes
---------------
Generic classes are declared as follows::
class Bag[T]: ...
This syntax is equivalent to::
annotation-def TYPE_PARAMS_OF_Bag():
T = typing.TypeVar("T")
class Bag(typing.Generic[T]):
__type_params__ = (T,)
...
return Bag
Bag = TYPE_PARAMS_OF_Bag()
Here again ``annotation-def`` (not a real keyword) indicates an
:ref:`annotation scope <annotation-scopes>`, and the name
``TYPE_PARAMS_OF_Bag`` is not actually bound at runtime.
Generic classes implicitly inherit from :data:`typing.Generic`.
The base classes and keyword arguments of generic classes are
evaluated within the type scope for the type parameters,
and decorators are evaluated outside that scope. This is illustrated
by this example::
@decorator
class Bag(Base[T], arg=T): ...
This is equivalent to::
annotation-def TYPE_PARAMS_OF_Bag():
T = typing.TypeVar("T")
class Bag(Base[T], typing.Generic[T], arg=T):
__type_params__ = (T,)
...
return Bag
Bag = decorator(TYPE_PARAMS_OF_Bag())
.. _generic-type-aliases:
Generic type aliases
--------------------
The :keyword:`type` statement can also be used to create a generic type alias::
type ListOrSet[T] = list[T] | set[T]
Except for the :ref:`lazy evaluation <lazy-evaluation>` of the value,
this is equivalent to::
annotation-def TYPE_PARAMS_OF_ListOrSet():
T = typing.TypeVar("T")
annotation-def VALUE_OF_ListOrSet():
return list[T] | set[T]
# In reality, the value is lazily evaluated
return typing.TypeAliasType("ListOrSet", VALUE_OF_ListOrSet(), type_params=(T,))
ListOrSet = TYPE_PARAMS_OF_ListOrSet()
Here, ``annotation-def`` (not a real keyword) indicates an
:ref:`annotation scope <annotation-scopes>`. The capitalized names
like ``TYPE_PARAMS_OF_ListOrSet`` are not actually bound at runtime.
.. rubric:: Footnotes

View File

@ -499,6 +499,7 @@ Callable types
single: __globals__ (function attribute)
single: __annotations__ (function attribute)
single: __kwdefaults__ (function attribute)
single: __type_params__ (function attribute)
pair: global; namespace
+-------------------------+-------------------------------+-----------+
@ -561,6 +562,12 @@ Callable types
| :attr:`__kwdefaults__` | A dict containing defaults | Writable |
| | for keyword-only parameters. | |
+-------------------------+-------------------------------+-----------+
| :attr:`__type_params__` | A tuple containing the | Writable |
| | :ref:`type parameters | |
| | <type-params>` of a | |
| | :ref:`generic function | |
| | <generic-functions>`. | |
+-------------------------+-------------------------------+-----------+
Most of the attributes labelled "Writable" check the type of the assigned value.
@ -837,6 +844,7 @@ Custom classes
single: __bases__ (class attribute)
single: __doc__ (class attribute)
single: __annotations__ (class attribute)
single: __type_params__ (class attribute)
Special attributes:
@ -863,6 +871,10 @@ Custom classes
working with :attr:`__annotations__`, please see
:ref:`annotations-howto`.
:attr:`__type_params__`
A tuple containing the :ref:`type parameters <type-params>` of
a :ref:`generic class <generic-classes>`.
Class instances
.. index::
pair: object; class instance

View File

@ -71,6 +71,8 @@ The following constructs bind names:
+ in a capture pattern in structural pattern matching
* :keyword:`import` statements.
* :keyword:`type` statements.
* :ref:`type parameter lists <type-params>`.
The :keyword:`!import` statement of the form ``from ... import *`` binds all
names defined in the imported module, except those beginning with an underscore.
@ -149,7 +151,8 @@ a global statement, the free variable is treated as a global.
The :keyword:`nonlocal` statement causes corresponding names to refer
to previously bound variables in the nearest enclosing function scope.
:exc:`SyntaxError` is raised at compile time if the given name does not
exist in any enclosing function scope.
exist in any enclosing function scope. :ref:`Type parameters <type-params>`
cannot be rebound with the :keyword:`!nonlocal` statement.
.. index:: pair: module; __main__
@ -163,14 +166,119 @@ These references follow the normal rules for name resolution with an exception
that unbound local variables are looked up in the global namespace.
The namespace of the class definition becomes the attribute dictionary of
the class. The scope of names defined in a class block is limited to the
class block; it does not extend to the code blocks of methods -- this includes
comprehensions and generator expressions since they are implemented using a
function scope. This means that the following will fail::
class block; it does not extend to the code blocks of methods. This includes
comprehensions and generator expressions, but it does not include
:ref:`annotation scopes <annotation-scopes>`,
which have access to their enclosing class scopes.
This means that the following will fail::
class A:
a = 42
b = list(a + i for i in range(10))
However, the following will succeed::
class A:
type Alias = Nested
class Nested: pass
print(A.Alias.__value__) # <type 'A.Nested'>
.. _annotation-scopes:
Annotation scopes
-----------------
:ref:`Type parameter lists <type-params>` and :keyword:`type` statements
introduce *annotation scopes*, which behave mostly like function scopes,
but with some exceptions discussed below. :term:`Annotations <annotation>`
currently do not use annotation scopes, but they are expected to use
annotation scopes in Python 3.13 when :pep:`649` is implemented.
Annotation scopes are used in the following contexts:
* Type parameter lists for :ref:`generic type aliases <generic-type-aliases>`.
* Type parameter lists for :ref:`generic functions <generic-functions>`.
A generic function's annotations are
executed within the annotation scope, but its defaults and decorators are not.
* Type parameter lists for :ref:`generic classes <generic-classes>`.
A generic class's base classes and
keyword arguments are executed within the annotation scope, but its decorators are not.
* The bounds and constraints for type variables
(:ref:`lazily evaluated <lazy-evaluation>`).
* The value of type aliases (:ref:`lazily evaluated <lazy-evaluation>`).
Annotation scopes differ from function scopes in the following ways:
* Annotation scopes have access to their enclosing class namespace.
If an annotation scope is immediately within a class scope, or within another
annotation scope that is immediately within a class scope, the code in the
annotation scope can use names defined in the class scope as if it were
executed directly within the class body. This contrasts with regular
functions defined within classes, which cannot access names defined in the class scope.
* Expressions in annotation scopes cannot contain :keyword:`yield`, ``yield from``,
:keyword:`await`, or :token:`:= <python-grammar:assignment_expression>`
expressions. (These expressions are allowed in other scopes contained within the
annotation scope.)
* Names defined in annotation scopes cannot be rebound with :keyword:`nonlocal`
statements in inner scopes. This includes only type parameters, as no other
syntactic elements that can appear within annotation scopes can introduce new names.
* While annotation scopes have an internal name, that name is not reflected in the
:term:`__qualname__ <qualified name>` of objects defined within the scope.
Instead, the :attr:`!__qualname__`
of such objects is as if the object were defined in the enclosing scope.
.. versionadded:: 3.12
Annotation scopes were introduced in Python 3.12 as part of :pep:`695`.
.. _lazy-evaluation:
Lazy evaluation
---------------
The values of type aliases created through the :keyword:`type` statement are
*lazily evaluated*. The same applies to the bounds and constraints of type
variables created through the :ref:`type parameter syntax <type-params>`.
This means that they are not evaluated when the type alias or type variable is
created. Instead, they are only evaluated when doing so is necessary to resolve
an attribute access.
Example:
.. doctest::
>>> type Alias = 1/0
>>> Alias.__value__
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
>>> def func[T: 1/0](): pass
>>> T = func.__type_params__[0]
>>> T.__bound__
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
Here the exception is raised only when the ``__value__`` attribute
of the type alias or the ``__bound__`` attribute of the type variable
is accessed.
This behavior is primarily useful for references to types that have not
yet been defined when the type alias or type variable is created. For example,
lazy evaluation enables creation of mutually recursive type aliases::
from typing import Literal
type SimpleExpr = int | Parenthesized
type Parenthesized = tuple[Literal["("], Expr, Literal[")"]]
type Expr = SimpleExpr | tuple[SimpleExpr, Literal["+", "-"], Expr]
Lazily evaluated values are evaluated in :ref:`annotation scope <annotation-scopes>`,
which means that names that appear inside the lazily evaluated value are looked up
as if they were used in the immediately enclosing scope.
.. versionadded:: 3.12
.. _restrict_exec:
Builtins and restricted execution

View File

@ -361,15 +361,19 @@ Soft Keywords
.. versionadded:: 3.10
Some identifiers are only reserved under specific contexts. These are known as
*soft keywords*. The identifiers ``match``, ``case`` and ``_`` can
syntactically act as keywords in contexts related to the pattern matching
statement, but this distinction is done at the parser level, not when
tokenizing.
*soft keywords*. The identifiers ``match``, ``case``, ``type`` and ``_`` can
syntactically act as keywords in certain contexts,
but this distinction is done at the parser level, not when tokenizing.
As soft keywords, their use with pattern matching is possible while still
preserving compatibility with existing code that uses ``match``, ``case`` and ``_`` as
As soft keywords, their use in the grammar is possible while still
preserving compatibility with existing code that uses these names as
identifier names.
``match``, ``case``, and ``_`` are used in the :keyword:`match` statement.
``type`` is used in the :keyword:`type` statement.
.. versionchanged:: 3.12
``type`` is now a soft keyword.
.. index::
single: _, identifiers

View File

@ -28,6 +28,7 @@ simple statements is:
: | `future_stmt`
: | `global_stmt`
: | `nonlocal_stmt`
: | `type_stmt`
.. _exprstmts:
@ -1012,3 +1013,48 @@ pre-existing bindings in the local scope.
:pep:`3104` - Access to Names in Outer Scopes
The specification for the :keyword:`nonlocal` statement.
.. _type:
The :keyword:`!type` statement
==============================
.. index:: pair: statement; type
.. productionlist:: python-grammar
type_stmt: 'type' `identifier` [`type_params`] "=" `expression`
The :keyword:`!type` statement declares a type alias, which is an instance
of :class:`typing.TypeAliasType`.
For example, the following statement creates a type alias::
type Point = tuple[float, float]
This code is roughly equivalent to::
annotation-def VALUE_OF_Point():
return tuple[float, float]
Point = typing.TypeAliasType("Point", VALUE_OF_Point())
``annotation-def`` indicates an :ref:`annotation scope <annotation-scopes>`, which behaves
mostly like a function, but with several small differences.
The value of the
type alias is evaluated in the annotation scope. It is not evaluated when the
type alias is created, but only when the value is accessed through the type alias's
:attr:`!__value__` attribute (see :ref:`lazy-evaluation`).
This allows the type alias to refer to names that are not yet defined.
Type aliases may be made generic by adding a :ref:`type parameter list <type-params>`
after the name. See :ref:`generic-type-aliases` for more.
:keyword:`!type` is a :ref:`soft keyword <soft-keywords>`.
.. versionadded:: 3.12
.. seealso::
:pep:`695` - Type Parameter Syntax
Introduced the :keyword:`!type` statement and syntax for
generic classes and functions.

View File

@ -74,6 +74,8 @@ New typing features:
* :pep:`688`: Making the buffer protocol accessible in Python
* :ref:`whatsnew312-pep695`
* :ref:`whatsnew312-pep692`
* :pep:`698`: Override Decorator for Static Typing
@ -272,6 +274,70 @@ See :pep:`692` for more details.
(PEP written by Franek Magiera)
.. _whatsnew312-pep695:
PEP 695: Type Parameter Syntax
------------------------------
Generic classes and functions under :pep:`484` were declared using a verbose syntax
that left the scope of type parameters unclear and required explicit declarations of
variance.
:pep:`695` introduces a new, more compact and explicit way to create
:ref:`generic classes <generic-classes>` and :ref:`functions <generic-functions>`::
def max[T](args: Iterable[T]) -> T:
...
class list[T]:
def __getitem__(self, index: int, /) -> T:
...
def append(self, element: T) -> None:
...
In addition, the PEP introduces a new way to declare :ref:`type aliases <type-aliases>`
using the :keyword:`type` statement, which creates an instance of
:class:`~typing.TypeAliasType`::
type Point = tuple[float, float]
Type aliases can also be :ref:`generic <generic-type-aliases>`::
type Point[T] = tuple[T, T]
The new syntax allows declaring :class:`~typing.TypeVarTuple`
and :class:`~typing.ParamSpec` parameters, as well as :class:`~typing.TypeVar`
parameters with bounds or constraints::
type IntFunc[**P] = Callable[P, int] # ParamSpec
type LabeledTuple[*Ts] = tuple[str, *Ts] # TypeVarTuple
type HashableSequence[T: Hashable] = Sequence[T] # TypeVar with bound
type IntOrStrSequence[T: (int, str)] = Sequence[T] # TypeVar with constraints
The value of type aliases and the bound and constraints of type variables
created through this syntax are evaluated only on demand (see
:ref:`lazy-evaluation`). This means type aliases are able to refer to other
types defined later in the file.
Type parameters declared through a type parameter list are visible within the
scope of the declaration and any nested scopes, but not in the outer scope. For
example, they can be used in the type annotations for the methods of a generic
class or in the class body. However, they cannot be used in the module scope after
the class is defined. See :ref:`type-params` for a detailed description of the
runtime semantics of type parameters.
In order to support these scoping semantics, a new kind of scope is introduced,
the :ref:`annotation scope <annotation-scopes>`. Annotation scopes behave for the
most part like function scopes, but interact differently with enclosing class scopes.
In Python 3.13, :term:`annotations <annotation>` will also be evaluated in
annotation scopes.
See :pep:`695` for more details.
(PEP written by Eric Traut. Implementation by Jelle Zijlstra, Eric Traut,
and others in :gh:`103764`.)
Other Language Changes
======================
@ -806,14 +872,19 @@ Optimizations
CPython bytecode changes
========================
* Removed the :opcode:`LOAD_METHOD` instruction. It has been merged into
* Remove the :opcode:`LOAD_METHOD` instruction. It has been merged into
:opcode:`LOAD_ATTR`. :opcode:`LOAD_ATTR` will now behave like the old
:opcode:`LOAD_METHOD` instruction if the low bit of its oparg is set.
(Contributed by Ken Jin in :gh:`93429`.)
* Removed the :opcode:`!JUMP_IF_FALSE_OR_POP` and :opcode:`!JUMP_IF_TRUE_OR_POP`
* Remove the :opcode:`!JUMP_IF_FALSE_OR_POP` and :opcode:`!JUMP_IF_TRUE_OR_POP`
instructions. (Contributed by Irit Katriel in :gh:`102859`.)
* Add the :opcode:`LOAD_FROM_DICT_OR_DEREF`, :opcode:`LOAD_FROM_DICT_OR_GLOBALS`,
and :opcode:`LOAD_LOCALS` opcodes as part of the implementation of :pep:`695`.
Remove the :opcode:`!LOAD_CLASSDEREF` opcode, which can be replaced with
:opcode:`LOAD_LOCALS` plus :opcode:`LOAD_FROM_DICT_OR_DEREF`. (Contributed
by Jelle Zijlstra in :gh:`103764`.)
Demos and Tools
===============

View File

@ -443,45 +443,38 @@ static PyMethodDef typevar_methods[] = {
PyDoc_STRVAR(typevar_doc,
"Type variable.\n\
\n\
Usage::\n\
The preferred way to construct a type variable is via the dedicated syntax\n\
for generic functions, classes, and type aliases:\n\
\n\
T = TypeVar('T') # Can be anything\n\
A = TypeVar('A', str, bytes) # Must be str or bytes\n\
class Sequence[T]: # T is a TypeVar\n\
...\n\
\n\
This syntax can also be used to create bound and constrained type\n\
variables:\n\
\n\
class StrSequence[S: str]: # S is a TypeVar bound to str\n\
...\n\
\n\
class StrOrBytesSequence[A: (str, bytes)]: # A is a TypeVar constrained to str or bytes\n\
...\n\
\n\
However, if desired, reusable type variables can also be constructed\n\
manually, like so:\n\
\n\
T = TypeVar('T') # Can be anything\n\
S = TypeVar('S', bound=str) # Can be any subtype of str\n\
A = TypeVar('A', str, bytes) # Must be exactly str or bytes\n\
\n\
Type variables exist primarily for the benefit of static type\n\
checkers. They serve as the parameters for generic types as well\n\
as for generic function definitions. See class Generic for more\n\
information on generic types. Generic functions work as follows:\n\
as for generic function and type alias definitions.\n\
\n\
def repeat(x: T, n: int) -> List[T]:\n\
'''Return a list containing n references to x.'''\n\
return [x]*n\n\
\n\
def longest(x: A, y: A) -> A:\n\
'''Return the longest of two strings.'''\n\
return x if len(x) >= len(y) else y\n\
\n\
The latter example's signature is essentially the overloading\n\
of (str, str) -> str and (bytes, bytes) -> bytes. Also note\n\
that if the arguments are instances of some subclass of str,\n\
the return type is still plain str.\n\
\n\
At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError.\n\
\n\
Type variables defined with covariant=True or contravariant=True\n\
can be used to declare covariant or contravariant generic types.\n\
See PEP 484 for more details. By default generic types are invariant\n\
in all type variables.\n\
\n\
Type variables can be introspected. e.g.:\n\
\n\
T.__name__ == 'T'\n\
T.__constraints__ == ()\n\
T.__covariant__ == False\n\
T.__contravariant__ = False\n\
A.__constraints__ == (str, bytes)\n\
\n\
Note that only type variables defined in global scope can be pickled.\n\
The variance of type variables is inferred by type checkers when they are created\n\
through the type parameter syntax and when ``infer_variance=True`` is passed.\n\
Manually created type variables may be explicitly marked covariant or\n\
contravariant by passing ``covariant=True`` or ``contravariant=True``.\n\
By default, manually created type variables are invariant. See PEP 484\n\
and PEP 695 for more details.\n\
");
static PyType_Slot typevar_slots[] = {
@ -942,7 +935,14 @@ static PyMethodDef paramspec_methods[] = {
PyDoc_STRVAR(paramspec_doc,
"Parameter specification variable.\n\
\n\
Usage::\n\
The preferred way to construct a parameter specification is via the dedicated syntax\n\
for generic functions, classes, and type aliases, where\n\
the use of '**' creates a parameter specification:\n\
\n\
type IntFunc[**P] = Callable[P, int]\n\
\n\
For compatibility with Python 3.11 and earlier, ParamSpec objects\n\
can also be created as follows:\n\
\n\
P = ParamSpec('P')\n\
\n\
@ -952,12 +952,9 @@ callable to another callable, a pattern commonly found in higher order\n\
functions and decorators. They are only valid when used in ``Concatenate``,\n\
or as the first argument to ``Callable``, or as parameters for user-defined\n\
Generics. See class Generic for more information on generic types. An\n\
example for annotating a decorator::\n\
example for annotating a decorator:\n\
\n\
T = TypeVar('T')\n\
P = ParamSpec('P')\n\
\n\
def add_logging(f: Callable[P, T]) -> Callable[P, T]:\n\
def add_logging[**P, T](f: Callable[P, T]) -> Callable[P, T]:\n\
'''A type-safe decorator to add logging to a function.'''\n\
def inner(*args: P.args, **kwargs: P.kwargs) -> T:\n\
logging.info(f'{f.__name__} was called')\n\
@ -969,17 +966,9 @@ example for annotating a decorator::\n\
'''Add two numbers together.'''\n\
return x + y\n\
\n\
Parameter specification variables defined with covariant=True or\n\
contravariant=True can be used to declare covariant or contravariant\n\
generic types. These keyword arguments are valid, but their actual semantics\n\
are yet to be decided. See PEP 612 for details.\n\
\n\
Parameter specification variables can be introspected. e.g.:\n\
\n\
P.__name__ == 'P'\n\
P.__bound__ == None\n\
P.__covariant__ == False\n\
P.__contravariant__ == False\n\
\n\
Note that only parameter specification variables defined in global scope can\n\
be pickled.\n\
@ -1175,9 +1164,18 @@ static PyMethodDef typevartuple_methods[] = {
};
PyDoc_STRVAR(typevartuple_doc,
"Type variable tuple.\n\
"Type variable tuple. A specialized form of type variable that enables\n\
variadic generics.\n\
\n\
Usage:\n\
The preferred way to construct a type variable tuple is via the dedicated syntax\n\
for generic functions, classes, and type aliases, where a single\n\
'*' indicates a type variable tuple:\n\
\n\
def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]:\n\
return (*tup[1:], tup[0])\n\
\n\
For compatibility with Python 3.11 and earlier, TypeVarTuple objects\n\
can also be created as follows:\n\
\n\
Ts = TypeVarTuple('Ts') # Can be given any name\n\
\n\
@ -1185,7 +1183,7 @@ Just as a TypeVar (type variable) is a placeholder for a single type,\n\
a TypeVarTuple is a placeholder for an *arbitrary* number of types. For\n\
example, if we define a generic class using a TypeVarTuple:\n\
\n\
class C(Generic[*Ts]): ...\n\
class C[*Ts]: ...\n\
\n\
Then we can parameterize that class with an arbitrary number of type\n\
arguments:\n\
@ -1441,6 +1439,23 @@ PyDoc_STRVAR(typealias_doc,
Type aliases are created through the type statement:\n\
\n\
type Alias = int\n\
\n\
In this example, Alias and int will be treated equivalently by static\n\
type checkers.\n\
\n\
At runtime, Alias is an instance of TypeAliasType. The __name__ attribute\n\
holds the name of the type alias. The value of the type\n\
alias is stored in the __value__ attribute. It is evaluated lazily, so\n\
the value is computed only if the attribute is accessed.\n\
\n\
Type aliases can also be generic:\n\
\n\
type ListOrSet[T] = list[T] | set[T]\n\
\n\
In this case, the type parameters of the alias are stored in the\n\
__type_params__ attribute.\n\
\n\
See PEP 695 for more information.\n\
");
static PyNumberMethods typealias_as_number = {
@ -1489,14 +1504,14 @@ PyDoc_STRVAR(generic_doc,
\n\
A generic type is typically declared by inheriting from\n\
this class parameterized with one or more type variables.\n\
For example, a generic mapping type might be defined as::\n\
For example, a generic mapping type might be defined as:\n\
\n\
class Mapping(Generic[KT, VT]):\n\
def __getitem__(self, key: KT) -> VT:\n\
...\n\
# Etc.\n\
\n\
This class can then be used as follows::\n\
This class can then be used as follows:\n\
\n\
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:\n\
try:\n\