@@ -93,8 +93,16 @@ This is equivalent to omitting the generic notation and just saying
9393User-defined generic types
9494--------------------------
9595
96- You can include a ``Generic `` base class to define a user-defined class
97- as generic. Example::
96+ There are several ways to define a user-defined class as generic:
97+
98+ * Include a ``Generic `` base class.
99+ * Use the new generic class syntax in Python 3.12 and higher.
100+ * Include a `` Protocol`` base class parameterized with type variables. This
101+ approach also marks the class as a protocol - see
102+ :ref: `generic protocols<generic-protocols> ` for more information.
103+ * Include a generic base class parameterized with type variables.
104+
105+ Example using ``Generic ``::
98106
99107 from typing import TypeVar, Generic
100108 from logging import Logger
@@ -118,14 +126,14 @@ as generic. Example::
118126 def log(self, message: str) -> None:
119127 self.logger.info('{}: {}'.format(self.name, message))
120128
121- Or, in Python 3.12 and higher, by using the new syntax for generic
122- classes::
129+ Or, using the new generic class syntax::
123130
124131 class LoggedVar[T]:
125132 # methods as in previous example
126133
127- This implicitly adds ``Generic[T] `` as a base class and type checkers
128- should treat the two largely equivalently (except for variance, see below).
134+ This implicitly adds ``Generic[T] `` as a base class, and type checkers
135+ should treat the two definitions of ``LoggedVar `` largely equivalently (except
136+ for variance, see below).
129137
130138``Generic[T] `` as a base class defines that the class ``LoggedVar ``
131139takes a single type parameter ``T ``. This also makes ``T `` valid as
@@ -144,7 +152,6 @@ A generic type can have any number of type variables, and type variables
144152may be constrained. This is valid::
145153
146154 from typing import TypeVar, Generic
147- ...
148155
149156 T = TypeVar('T')
150157 S = TypeVar('S')
@@ -156,29 +163,52 @@ Each type variable argument to ``Generic`` must be distinct. This is
156163thus invalid::
157164
158165 from typing import TypeVar, Generic
159- ...
160166
161167 T = TypeVar('T')
162168
163169 class Pair(Generic[T, T]): # INVALID
164170 ...
165171
166- The ``Generic[T] `` base class is redundant in simple cases where you
167- subclass some other generic class and specify type variables for its
168- parameters::
172+ All arguments to ``Generic `` or ``Protocol `` must be type variables::
169173
170- from typing import TypeVar
171- from collections.abc import Iterator
174+ from typing import Generic, Protocol
175+
176+ class Bad1(Generic[int]): # INVALID
177+ ...
178+ class Bad2(Protocol[int]): # INVALID
179+ ...
180+
181+ When a ``Generic `` or parameterized ``Protocol `` base class is present, all type
182+ parameters for the class must appear within the ``Generic `` or
183+ ``Protocol `` type argument list, respectively. A type checker should report an
184+ error if a type variable that is not included in the type argument list appears
185+ elsewhere in the base class list::
186+
187+ from typing import Generic, Protocol, TypeVar
188+ from collections.abc import Iterable
172189
173190 T = TypeVar('T')
191+ S = TypeVar('S')
174192
175- class MyIter(Iterator[T]):
193+ class Bad1(Iterable[T], Generic[S]): # INVALID
194+ ...
195+ class Bad2(Iterable[T], Protocol[S]): # INVALID
176196 ...
177197
178- That class definition is equivalent to::
198+ Note that the above rule does not apply to a bare ``Protocol `` base class. This
199+ is valid (see below)::
179200
180- class MyIter(Iterator[T], Generic[T]):
181- ...
201+ from typing import Protocol, TypeVar
202+ from collections.abc import Iterator
203+
204+ T = TypeVar('T')
205+
206+ class MyIterator(Iterator[T], Protocol): ...
207+
208+ When no ``Generic `` or parameterized ``Protocol `` base class is present, a
209+ defined class is generic if you subclass one or more other generic classes and
210+ specify type variables for their parameters. See :ref: `generic-base-classes `
211+ for details.
182212
183213You can use multiple inheritance with ``Generic ``::
184214
@@ -402,6 +432,7 @@ instead is preferred. (First, creating the subscripted class,
402432e.g. ``Node[int] ``, has a runtime cost. Second, using a type alias
403433is more readable.)
404434
435+ .. _`generic-base-classes` :
405436
406437Arbitrary generic types as base classes
407438---------------------------------------
@@ -458,8 +489,44 @@ Also consider the following example::
458489 class MyDict(Mapping[str, T]):
459490 ...
460491
461- In this case MyDict has a single parameter, T.
492+ In this case ``MyDict `` has a single type parameter, ``T ``.
493+
494+ Type variables are applied to the defined class in the order in which
495+ they first appear in any generic base classes::
496+
497+ from typing import Generic, TypeVar
498+
499+ T1 = TypeVar('T1')
500+ T2 = TypeVar('T2')
501+ T3 = TypeVar('T3')
502+
503+ class Parent1(Generic[T1, T2]):
504+ ...
505+ class Parent2(Generic[T1, T2]):
506+ ...
507+ class Child(Parent1[T1, T3], Parent2[T2, T3]):
508+ ...
509+
510+ That ``Child `` definition is equivalent to::
462511
512+ class Child(Parent1[T1, T3], Parent2[T2, T3], Generic[T1, T3, T2]):
513+ ...
514+
515+ A type checker should report an error when the type variable order is
516+ inconsistent::
517+
518+ from typing import Generic, TypeVar
519+
520+ T1 = TypeVar('T1')
521+ T2 = TypeVar('T2')
522+ T3 = TypeVar('T3')
523+
524+ class Grandparent(Generic[T1, T2]):
525+ ...
526+ class Parent(Grandparent[T1, T2]):
527+ ...
528+ class Child(Parent[T1, T2], Grandparent[T2, T1]): # INVALID
529+ ...
463530
464531Abstract generic types
465532----------------------
0 commit comments