@@ -130,11 +130,142 @@ Example:
130
130
>> > assert isinstance (argument, Some)
131
131
>> > assert some(argument) == 2
132
132
133
- .. note ::
133
+ .. warning ::
134
134
135
135
It is impossible for ``mypy `` to understand that ``1 `` has ``Some ``
136
136
type in this example. Be careful, it might break your code!
137
137
138
+ This example is not really useful on its own,
139
+ because as it was said, it can break things.
140
+
141
+ Instead, we are going to learn about
142
+ how this feature can be used to model
143
+ your domain model precisely with delegates.
144
+
145
+
146
+ Delegates
147
+ ---------
148
+
149
+ Let's say that you want to handle types like ``List[int] `` with ``classes ``.
150
+ The simple approach won't work, because Python cannot tell
151
+ that some ``list `` is ``List[int] `` or ``List[str] ``:
152
+
153
+ .. code :: python
154
+
155
+ >> > from typing import List
156
+
157
+ >> > isinstance ([1 , 2 , 3 ], List[int ])
158
+ Traceback (most recent call last):
159
+ ...
160
+ TypeError : Subscripted generics cannot be used with class and instance checks
161
+
162
+ We need some custom type inference mechanism:
163
+
164
+ .. code :: python
165
+
166
+ >> > from typing import List
167
+
168
+ >> > class _ListOfIntMeta (type ):
169
+ ... def __instancecheck__ (self , arg ) -> bool :
170
+ ... return (
171
+ ... isinstance (other, list ) and
172
+ ... all (isinstance (item, int ) for item in arg
173
+ ... )
174
+
175
+ >> > class ListOfInt(List[int ], metaclass = _ListOfIntMeta):
176
+ ... ...
177
+
178
+ Now we can be sure that our `` List[int ]`` can be checked in runtime:
179
+
180
+ .. code:: python
181
+
182
+ >> > assert isinstance ([1 , 2 , 3 ], ListOfInt) is True
183
+ >> > assert isinstance ([1 , ' a' ], ListOfInt) is False
184
+
185
+ And now we can use it with `` classes`` :
186
+
187
+ .. code:: python
188
+
189
+ >> > from classes import typeclass
190
+
191
+ >> > @ typeclass
192
+ ... def sum_all(instance) -> int :
193
+ ... ...
194
+
195
+ >> > @ sum_all.instance(ListOfInt)
196
+ ... def _sum_all_list_int(instance: ListOfInt) -> int :
197
+ ... return sum (instance)
198
+
199
+ >> > your_list = [1 , 2 , 3 ]
200
+ >> > if isinstance (your_list, ListOfInt):
201
+ ... sum_all(your_list)
202
+
203
+ This solution still has several problems:
204
+
205
+ 1 . Notice, that you have to use `` if isinstance `` or `` assert isinstance `` here.
206
+ Because otherwise `` mypy`` won' t be happy without it,
207
+ type won' t be narrowed to ``ListOfInt`` from ``List[int]``.
208
+ This does not feel right.
209
+ 2 . `` ListOfInt`` is very verbose, it even has a metaclass!
210
+ 3 . There' s a typing mismatch: in runtime ``your_list`` would be ``List[int]``
211
+ and `` mypy`` thinks that it is `` ListOfInt``
212
+ (a fake type that we are not ever using directly)
213
+
214
+ To solve all these problems we recommend to use `` phantom- types`` package.
215
+
216
+ First, you need to define a " phantom" type
217
+ (it is called " phantom" because it does not exist in runtime):
218
+
219
+ .. code:: python
220
+
221
+ >> > from phantom import Phantom
222
+ >> > from phantom.predicates import collection, generic
223
+
224
+ >> > class ListOfInt(
225
+ ... List[int ],
226
+ ... Phantom,
227
+ ... predicate = collection.every(generic.of_type(int )),
228
+ ... ):
229
+ ... ...
230
+
231
+ >> > assert isinstance ([1 , 2 , 3 ], ListOfInt)
232
+ >> > assert type ([1 , 2 , 3 ]) is list
233
+
234
+ Short, easy, and readable.
235
+
236
+ Now, we can define our typeclass with " phantom" type support:
237
+
238
+ .. code:: python
239
+
240
+ >> > from classes import typeclass
241
+
242
+ >> > @ typeclass
243
+ ... def sum_all(instance) -> int :
244
+ ... ...
245
+
246
+ >> > @ sum_all.instance(List[int ], delegate = ListOfInt)
247
+ ... def _sum_all_list_int(instance: List[int ]) -> int :
248
+ ... return sum (instance)
249
+
250
+ >> > assert sum_all([1 , 2 , 3 ]) == 6
251
+
252
+ That' s why we need a ``delegate=`` argument here:
253
+ we don' t really work with ``List[int]``,
254
+ we delegate all the runtime type checking to `` ListOfInt`` phantom type .
255
+
256
+ Performance considerations
257
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
258
+
259
+ Traversing the whole list to check that all elements
260
+ are of the given type can be really slow.
261
+
262
+ You might need a different algorithm.
263
+ Take a look at `beartype < https:// github.com/ beartype/ beartype> ` _.
264
+ It promises runtime type checking with `` O(1 )`` non- amortized worst- case time
265
+ with negligible constant factors.
266
+
267
+ Take a look at their docs to learn more.
268
+
138
269
139
270
Type resolution order
140
271
-------------------- -
0 commit comments