From 2d4174934b73d18d6b0389b0d95f7e98ea79db22 Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Tue, 7 Apr 2026 10:30:46 +0530 Subject: [PATCH 1/7] Update generics reference to use PEP 695 syntax --- docs/reference/generics.rst | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index c39d2bc11..a50d6eda2 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -6,6 +6,13 @@ code. These types are interesting in that they are parametrised by other types! A ``list[str]`` isn't just a list, it's a list of strings. Types with type parameters like this are called *generic types*. +.. note:: + + Python 3.12 introduced :pep:`695`, which allows defining generics using + type parameter syntax (e.g., ``class Foo[T]:`` and ``def func[T](x: T) -> T:``). + The older ``TypeVar`` and ``Generic``-based syntax remains supported + for compatibility with earlier Python versions. + You can define your own generic classes that take type parameters, similar to built-in types such as ``list[X]``. Note that such user-defined generics are a moderately advanced feature and you can get far without ever using them. @@ -19,11 +26,7 @@ Here is a very simple generic class that represents a stack: .. code-block:: python - from typing import TypeVar, Generic - - T = TypeVar('T') - - class Stack(Generic[T]): + class Stack[T]: def __init__(self) -> None: # Create an empty list with items of type T self.items: list[T] = [] @@ -37,6 +40,17 @@ Here is a very simple generic class that represents a stack: def empty(self) -> bool: return not self.items +For compatibility with older Python versions, the same class may be written as: + +.. code-block:: python + + from typing import TypeVar, Generic + + T = TypeVar('T') + + class Stack(Generic[T]): + ... + The ``Stack`` class can be used to represent a stack of any type: ``Stack[int]``, ``Stack[tuple[int, str]]``, etc. @@ -56,7 +70,7 @@ construction of the instance will be type checked correspondingly. .. code-block:: python - class Box(Generic[T]): + class Box[T]: def __init__(self, content: T) -> None: self.content = content @@ -96,12 +110,12 @@ can be used as a base class for another class (generic or non-generic). For exam data: StrDict[int, int] # error: "StrDict" expects no type arguments, but 2 given data2: StrDict # OK - # This is a user-defined generic class - class Receiver(Generic[T]): - def accept(self, value: T) -> None: ... + # This is a user-defined generic class + class Receiver(Generic[T]): + def accept(self, value: T) -> None: ... - # This is a generic subclass of Receiver - class AdvancedReceiver(Receiver[T]): ... + # This is a generic subclass of Receiver + class AdvancedReceiver(Receiver[T]): ... .. note:: From 0225ec10215dc16c1e44453d959cb0d5f83bb85f Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Wed, 8 Apr 2026 12:41:30 +0530 Subject: [PATCH 2/7] Use PEP 695 syntax consistently in generics examples --- docs/reference/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index a50d6eda2..e2cdf9e3b 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -111,7 +111,7 @@ can be used as a base class for another class (generic or non-generic). For exam data2: StrDict # OK # This is a user-defined generic class - class Receiver(Generic[T]): + class Receiver[T]: def accept(self, value: T) -> None: ... # This is a generic subclass of Receiver From de35f89fd7520c58f266fcec6f9d7a8380604ceb Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Wed, 8 Apr 2026 16:28:04 +0530 Subject: [PATCH 3/7] Refine generics note per review feedback --- docs/reference/generics.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index e2cdf9e3b..f389c373c 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -6,13 +6,6 @@ code. These types are interesting in that they are parametrised by other types! A ``list[str]`` isn't just a list, it's a list of strings. Types with type parameters like this are called *generic types*. -.. note:: - - Python 3.12 introduced :pep:`695`, which allows defining generics using - type parameter syntax (e.g., ``class Foo[T]:`` and ``def func[T](x: T) -> T:``). - The older ``TypeVar`` and ``Generic``-based syntax remains supported - for compatibility with earlier Python versions. - You can define your own generic classes that take type parameters, similar to built-in types such as ``list[X]``. Note that such user-defined generics are a moderately advanced feature and you can get far without ever using them. @@ -40,6 +33,12 @@ Here is a very simple generic class that represents a stack: def empty(self) -> bool: return not self.items +.. note:: + + The type parameter syntax (e.g., ``class Foo[T]:``) was introduced in Python 3.12. + For earlier Python versions, generic classes need to be defined using + ``TypeVar`` and ``Generic``, as shown below. + For compatibility with older Python versions, the same class may be written as: .. code-block:: python From c6241881eaffc26abc4cff56b7164d0f9140c22f Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Wed, 8 Apr 2026 17:05:26 +0530 Subject: [PATCH 4/7] Final refinements: wording and PEP 695 consistency in generics docs --- docs/reference/generics.rst | 46 ++++++++++++------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index f389c373c..0732671b3 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -35,7 +35,7 @@ Here is a very simple generic class that represents a stack: .. note:: - The type parameter syntax (e.g., ``class Foo[T]:``) was introduced in Python 3.12. + The type parameter syntax (e.g., ``class Foo[T]:``) is available in Python 3.12 and newer. For earlier Python versions, generic classes need to be defined using ``TypeVar`` and ``Generic``, as shown below. @@ -83,17 +83,14 @@ Defining subclasses of generic classes ************************************** User-defined generic classes and generic classes defined in :py:mod:`typing` -can be used as a base class for another class (generic or non-generic). For example: +can be used as base classes for other classes (generic or non-generic). For example: .. code-block:: python - from typing import Generic, TypeVar, Mapping, Iterator - - KT = TypeVar('KT') - VT = TypeVar('VT') + from typing import Mapping, Iterator # This is a generic subclass of Mapping - class MyMap(Mapping[KT, VT]): + class MyMap[KT, VT](Mapping[KT, VT]): def __getitem__(self, k: KT) -> VT: ... def __iter__(self) -> Iterator[KT]: ... def __len__(self) -> int: ... @@ -124,33 +121,16 @@ can be used as a base class for another class (generic or non-generic). For exam protocols like :py:class:`~typing.Iterable`, which use :ref:`structural subtyping `. -:py:class:`Generic ` can be omitted from bases if there are -other base classes that include type variables, such as ``Mapping[KT, VT]`` -in the above example. If you include ``Generic[...]`` in bases, then -it should list all type variables present in other bases (or more, -if needed). The order of type variables is defined by the following -rules: - -* If ``Generic[...]`` is present, then the order of variables is - always determined by their order in ``Generic[...]``. -* If there are no ``Generic[...]`` in bases, then all type variables - are collected in the lexicographic order (i.e. by first appearance). - For example: .. code-block:: python - from typing import Generic, TypeVar, Any - - T = TypeVar('T') - S = TypeVar('S') - U = TypeVar('U') + from typing import Any + class One[T]: ... + class Another[T]: ... - class One(Generic[T]): ... - class Another(Generic[T]): ... - - class First(One[T], Another[S]): ... - class Second(One[T], Another[S], Generic[S, U, T]): ... + class First[T, S](One[T], Another[S]): ... + class Second[S, U, T](One[T], Another[S]): ... x: First[int, str] # Here T is bound to int, S is bound to str y: Second[int, str, Any] # Here T is Any, S is int, and U is str @@ -218,8 +198,12 @@ the class definition. .. code-block:: python - # T is the type variable bound by this class - class PairedBox(Generic[T]): + from typing import TypeVar + + S = TypeVar('S') + + # T is the type parameter bound by this class + class PairedBox[T]: def __init__(self, content: T) -> None: self.content = content From f8d68e27c6f5aae59602c5a42c967e3d7d4845ca Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Wed, 15 Apr 2026 23:06:43 +0530 Subject: [PATCH 5/7] Fix generic subclass to include type parameters --- docs/reference/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 0732671b3..477398a2c 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -111,7 +111,7 @@ can be used as base classes for other classes (generic or non-generic). For exam def accept(self, value: T) -> None: ... # This is a generic subclass of Receiver - class AdvancedReceiver(Receiver[T]): ... + class AdvancedReceiver[T](Receiver[T]): ... .. note:: From c0d814c319f7decd6439579ad12fae6b50850a52 Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Thu, 23 Apr 2026 00:18:40 +0530 Subject: [PATCH 6/7] Use PEP 695 syntax consistently in generics examples and functions --- docs/reference/generics.rst | 51 +++++++++++++------------------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 477398a2c..a87d1b14b 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -145,12 +145,10 @@ where the types of the arguments or return value have some relationship: .. code-block:: python - from typing import TypeVar, Sequence - - T = TypeVar('T') + from typing import Sequence # A generic function! - def first(seq: Sequence[T]) -> T: + def first[T](seq: Sequence[T]) -> T: return seq[0] As with generic classes, the type variable can be replaced with any @@ -174,14 +172,12 @@ functions do not share any typing relationship to each other: .. code-block:: python - from typing import TypeVar, Sequence - - T = TypeVar('T') + from typing import Sequence - def first(seq: Sequence[T]) -> T: + def first[T](seq: Sequence[T]) -> T: return seq[0] - def last(seq: Sequence[T]) -> T: + def last[T](seq: Sequence[T]) -> T: return seq[-1] Variables should not have a type variable in their type unless the type variable @@ -198,20 +194,16 @@ the class definition. .. code-block:: python - from typing import TypeVar - - S = TypeVar('S') - # T is the type parameter bound by this class class PairedBox[T]: def __init__(self, content: T) -> None: self.content = content - # S is a type variable bound only in this method - def first(self, x: list[S]) -> S: + # S is a type parameter bound only in this method + def first[S](self, x: list[S]) -> S: return x[0] - def pair_with_first(self, x: list[S]) -> tuple[S, T]: + def pair_with_first[S](self, x: list[S]) -> tuple[S, T]: return (x[0], self.content) box = PairedBox("asdf") @@ -225,12 +217,8 @@ methods: .. code-block:: python - from typing import TypeVar - - T = TypeVar('T', bound='Shape') - class Shape: - def set_scale(self: T, scale: float) -> T: + def set_scale[T: Shape](self: T, scale: float) -> T: self.scale = scale return self @@ -256,15 +244,13 @@ For class methods, you can also define generic ``cls``, using :py:class:`type`: .. code-block:: python - from typing import Optional, TypeVar, Type - - T = TypeVar('T', bound='Friend') + from typing import Optional class Friend: other: Optional["Friend"] = None @classmethod - def make_pair(cls: type[T]) -> tuple[T, T]: + def make_pair[T: Friend](cls: type[T]) -> tuple[T, T]: a, b = cls(), cls() a.other = b b.other = a @@ -455,25 +441,22 @@ It's therefore often useful to be able to limit the types that a type variable can take on, for instance, by restricting it to values that are subtypes of a specific type. -Such a type is called the upper bound of the type variable, and is specified -with the ``bound=...`` keyword argument to :py:class:`~typing.TypeVar`. +Such a type is called the upper bound of the type parameter. In modern Python +(3.12+), this can be expressed directly in the type parameter list using +``[T: Bound]`` syntax. .. code-block:: python - from typing import TypeVar, SupportsAbs + from typing import SupportsAbs - T = TypeVar('T', bound=SupportsAbs[float]) + def largest_in_absolute_value[T: SupportsAbs[float]](*xs: T) -> T: + return max(xs, key=abs) In the definition of a generic function that uses such a type variable ``T``, the type represented by ``T`` is assumed to be a subtype of its upper bound, so the function can use methods of the upper bound on values of type ``T``. -.. code-block:: python - - def largest_in_absolute_value(*xs: T) -> T: - return max(xs, key=abs) # Okay, because T is a subtype of SupportsAbs[float]. - In a call to such a function, the type ``T`` must be replaced by a type that is a subtype of its upper bound. Continuing the example above: From 5310f7e6cda3d3a7962fd2e6e0de3535b39a4473 Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Sat, 25 Apr 2026 10:47:54 +0530 Subject: [PATCH 7/7] Fix invariance statement and add missing imports in variance examples --- docs/reference/generics.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index a87d1b14b..49017db56 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -335,10 +335,12 @@ Let us illustrate this by few simple examples: .. code-block:: python - # We'll use these classes in the examples below - class Shape: ... - class Triangle(Shape): ... - class Square(Shape): ... + from typing import Sequence, Union, Callable + + # We'll use these classes in the examples below + class Shape: ... + class Triangle(Shape): ... + class Square(Shape): ... * Most immutable containers, such as :py:class:`~typing.Sequence` and :py:class:`~typing.FrozenSet` are covariant. :py:data:`~typing.Union` is @@ -405,7 +407,9 @@ Let us illustrate this by few simple examples: Another example of an invariant type is :py:class:`~typing.Dict`. Most mutable containers are invariant. -By default, all user-defined generics are invariant. +The variance of user-defined generics depends on how type parameters +are used. Explicit variance annotations are only required when using +TypeVar. To declare a given generic class as covariant or contravariant use type variables defined with special keyword arguments ``covariant`` or ``contravariant``. For example: