-
Notifications
You must be signed in to change notification settings - Fork 297
Expand file tree
/
Copy pathgenerics_basic.py
More file actions
209 lines (116 loc) · 4.76 KB
/
generics_basic.py
File metadata and controls
209 lines (116 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
"""
Tests for basic usage of generics.
"""
# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#introduction
from __future__ import annotations
from collections.abc import Sequence
from typing import Any, Generic, Protocol, TypeVar, assert_type
T = TypeVar("T")
# > Generics can be parameterized by using a factory available in
# > ``typing`` called ``TypeVar``.
def first(l: Sequence[T]) -> T:
return l[0]
def test_first(seq_int: Sequence[int], seq_str: Sequence[str]) -> None:
assert_type(first(seq_int), int)
assert_type(first(seq_str), str)
# > ``TypeVar`` supports constraining parametric types to a fixed set of
# > possible types
AnyStr = TypeVar("AnyStr", str, bytes)
def concat(x: AnyStr, y: AnyStr) -> AnyStr:
return x + y
def test_concat(s: str, b: bytes, a: Any) -> None:
concat(s, s) # OK
concat(b, b) # OK
concat(s, b) # E
concat(b, s) # E
concat(s, a) # OK
concat(a, b) # OK
# > Specifying a single constraint is disallowed.
BadConstraint1 = TypeVar("BadConstraint1", str) # E
# > Note: those types cannot be parameterized by type variables
class Test(Generic[T]):
BadConstraint2 = TypeVar("BadConstraint2", str, list[T]) # E
# > Subtypes of types constrained by a type variable should be treated
# > as their respective explicitly listed base types in the context of the
# > type variable.
class MyStr(str): ...
def test_concat_subtype(s: str, b: bytes, a: Any, m: MyStr) -> None:
assert_type(concat(m, m), str)
assert_type(concat(m, s), str)
concat(m, b) # E
# TODO: should these be str or Any?
# reveal_type(concat(m, a))
# reveal_type(concat(a, m))
# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#user-defined-generic-classes
# > You can include a ``Generic`` base class to define a user-defined class
# > as generic.
from logging import Logger
from collections.abc import Iterable
class LoggedVar(Generic[T]):
def __init__(self, value: T, name: str, logger: Logger) -> None:
self.name = name
self.logger = logger
self.value = value
def set(self, new: T) -> None:
self.log("Set " + repr(self.value))
self.value = new
def get(self) -> T:
self.log("Get " + repr(self.value))
return self.value
def log(self, message: str) -> None:
self.logger.info("{}: {}".format(self.name, message))
def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
for var in vars:
var.set(0)
assert_type(var.get(), int)
# > A generic type can have any number of type variables, and type variables
# > may be constrained.
S = TypeVar("S")
class Pair1(Generic[T, S]): ...
# > Each type variable argument to ``Generic`` must be distinct.
class Pair2(Generic[T, T]): # E
...
# > The ``Generic[T]`` base class is redundant in simple cases where you
# > subclass some other generic class and specify type variables for its
# > parameters.
from collections.abc import Iterator, Mapping
class MyIter1(Iterator[T]): ...
class MyIter2(Iterator[T], Generic[T]): ...
def test_my_iter(m1: MyIter1[int], m2: MyIter2[int]):
assert_type(next(m1), int)
assert_type(next(m2), int)
K = TypeVar("K")
V = TypeVar("V")
class MyMap1(Mapping[K, V], Generic[K, V]): ...
class MyMap2(Mapping[K, V], Generic[V, K]): ...
def test_my_map(m1: MyMap1[str, int], m2: MyMap2[int, str]):
assert_type(m1["key"], int)
assert_type(m2["key"], int)
m1[0] # E
m2[0] # E
# > All arguments to ``Generic`` or ``Protocol`` must be type variables.
class Bad1(Generic[int]): ... # E
class Bad2(Protocol[int]): ... # E
# > All type parameters for the class must appear within the ``Generic`` or
# > ``Protocol`` type argument list.
T_co = TypeVar("T_co", covariant=True)
S_co = TypeVar("S_co", covariant=True)
class Bad3(Iterable[T_co], Generic[S_co]): ... # E
class Bad4(Iterable[T_co], Protocol[S_co]): ... # E
# > The above rule does not apply to a bare ``Protocol`` base class.
class MyIterator(Iterator[T_co], Protocol): ... # OK
# > You can use multiple inheritance with ``Generic``
from collections.abc import Sized, Container
class LinkedList(Sized, Generic[T]): ...
class MyMapping(Iterable[tuple[K, V]], Container[tuple[K, V]], Generic[K, V]): ...
# > Subclassing a generic class without specifying type parameters assumes
# > ``Any`` for each position. In the following example, ``MyIterable``
# > is not generic but implicitly inherits from ``Iterable[Any]``::
class MyIterableAny(Iterable): # Same as Iterable[Any]
...
def test_my_iterable_any(m: MyIterableAny):
assert_type(iter(m), Iterator[Any])
# > Generic metaclasses are not supported
class GenericMeta(type, Generic[T]): ...
class GenericMetaInstance(metaclass=GenericMeta[T]): # E
...