mirror of https://github.com/python/cpython
gh-103921: Document PEP 695 (#104642)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
95f1b1fef7
commit
060277d96b
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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` |
|
||||
+----------------------------------+---------------+-------------------+----------------+
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
===============
|
||||
|
|
|
@ -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\
|
||||
|
|
Loading…
Reference in New Issue