@@ -93,8 +93,16 @@ This is equivalent to omitting the generic notation and just saying
93
93
User-defined generic types
94
94
--------------------------
95
95
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 ``::
98
106
99
107
from typing import TypeVar, Generic
100
108
from logging import Logger
@@ -118,14 +126,14 @@ as generic. Example::
118
126
def log(self, message: str) -> None:
119
127
self.logger.info('{}: {}'.format(self.name, message))
120
128
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::
123
130
124
131
class LoggedVar[T]:
125
132
# methods as in previous example
126
133
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).
129
137
130
138
``Generic[T] `` as a base class defines that the class ``LoggedVar ``
131
139
takes 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
144
152
may be constrained. This is valid::
145
153
146
154
from typing import TypeVar, Generic
147
- ...
148
155
149
156
T = TypeVar('T')
150
157
S = TypeVar('S')
@@ -156,29 +163,52 @@ Each type variable argument to ``Generic`` must be distinct. This is
156
163
thus invalid::
157
164
158
165
from typing import TypeVar, Generic
159
- ...
160
166
161
167
T = TypeVar('T')
162
168
163
169
class Pair(Generic[T, T]): # INVALID
164
170
...
165
171
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::
169
173
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
172
189
173
190
T = TypeVar('T')
191
+ S = TypeVar('S')
174
192
175
- class MyIter(Iterator[T]):
193
+ class Bad1(Iterable[T], Generic[S]): # INVALID
194
+ ...
195
+ class Bad2(Iterable[T], Protocol[S]): # INVALID
176
196
...
177
197
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)::
179
200
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.
182
212
183
213
You can use multiple inheritance with ``Generic ``::
184
214
@@ -402,6 +432,7 @@ instead is preferred. (First, creating the subscripted class,
402
432
e.g. ``Node[int] ``, has a runtime cost. Second, using a type alias
403
433
is more readable.)
404
434
435
+ .. _`generic-base-classes` :
405
436
406
437
Arbitrary generic types as base classes
407
438
---------------------------------------
@@ -458,8 +489,44 @@ Also consider the following example::
458
489
class MyDict(Mapping[str, T]):
459
490
...
460
491
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::
462
511
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
+ ...
463
530
464
531
Abstract generic types
465
532
----------------------
0 commit comments