@@ -151,7 +151,174 @@ concrete. As with normal overrides, a dynamically typed method can
151
151
implement a statically typed abstract method defined in an abstract
152
152
base class.
153
153
154
+ .. _protocol-types :
155
+
156
+ Protocols and structural subtyping
157
+ **********************************
158
+
159
+ .. note ::
160
+
161
+ The support for structural subtyping is still experimental. Some features
162
+ might be not yet implemented, mypy could pass unsafe code or reject
163
+ working code.
164
+
165
+ There are two main type systems with respect to subtyping: nominal subtyping
166
+ and structural subtyping. The *nominal * subtyping is based on class hierarchy,
167
+ so that if class ``D `` inherits from class ``C ``, then it is a subtype
168
+ of ``C ``. This type system is primarily used in mypy since it allows
169
+ to produce clear and concise error messages, and since Python provides native
170
+ ``isinstance() `` checks based on class hierarchy. The *structural * subtyping
171
+ however has its own advantages. In this system class ``D `` is a subtype
172
+ of class ``C `` if the former has all attributes of the latter with
173
+ compatible types.
174
+
175
+ This type system is a static equivalent of duck typing, well known by Python
176
+ programmers. Mypy provides an opt-in support for structural subtyping via
177
+ protocol classes described in this section.
178
+ See `PEP 544 <https://www.python.org/dev/peps/pep-0544/ >`_ for
179
+ specification of protocols and structural subtyping in Python.
180
+
181
+ User defined protocols
182
+ **********************
183
+
184
+ To define a protocol class, one must inherit the special
185
+ ``typing_extensions.Protocol `` class:
186
+
187
+ .. code-block :: python
188
+
189
+ from typing import Iterable
190
+ from typing_extensions import Protocol
191
+
192
+ class SupportsClose (Protocol ):
193
+ def close (self ) -> None :
194
+ ...
195
+
196
+ class Resource : # Note, this class does not have 'SupportsClose' base.
197
+ # some methods
198
+ def close (self ) -> None :
199
+ self .resource.release()
200
+
201
+ def close_all (things : Iterable[SupportsClose]) -> None :
202
+ for thing in things:
203
+ thing.close()
204
+
205
+ close_all([Resource(), open (' some/file' )]) # This passes type check
206
+
207
+ .. note ::
208
+
209
+ The ``Protocol `` base class is currently provided in ``typing_extensions ``
210
+ package. When structural subtyping is mature and
211
+ `PEP 544 <https://www.python.org/dev/peps/pep-0544/ >`_ is accepted,
212
+ ``Protocol `` will be included in the ``typing `` module. As well, several
213
+ types such as ``typing.Sized ``, ``typing.Iterable `` etc. will be made
214
+ protocols.
215
+
216
+ Defining subprotocols
217
+ *********************
218
+
219
+ Subprotocols are also supported. Existing protocols can be extended
220
+ and merged using multiple inheritance. For example:
221
+
222
+ .. code-block :: python
223
+
224
+ # continuing from previous example
225
+
226
+ class SupportsRead (Protocol ):
227
+ def read (self , amount : int ) -> bytes : ...
228
+
229
+ class TaggedReadableResource (SupportsClose , SupportsRead , Protocol ):
230
+ label: str
231
+
232
+ class AdvancedResource (Resource ):
233
+ def __init__ (self , label : str ) -> None :
234
+ self .label = label
235
+ def read (self , amount : int ) -> bytes :
236
+ # some implementation
237
+ ...
238
+
239
+ resource = None # type: TaggedReadableResource
240
+
241
+ # some code
242
+
243
+ resource = AdvancedResource(' handle with care' ) # OK
244
+
245
+ Note that inheriting from existing protocols does not automatically turn
246
+ a subclass into a protocol, it just creates a usual (non-protocol) ABC that
247
+ implements given protocols. The ``typing_extensions.Protocol `` base must always
248
+ be explicitly present:
249
+
250
+ .. code-block :: python
251
+
252
+ class NewProtocol (SupportsClose ): # This is NOT a protocol
253
+ new_attr: int
254
+
255
+ class Concrete :
256
+ new_attr = None # type: int
257
+ def close (self ) -> None :
258
+ ...
259
+ # Below is an error, since nominal subtyping is used by default
260
+ x = Concrete() # type: NewProtocol # Error!
261
+
262
+ .. note ::
263
+
264
+ The `PEP 526 <https://www.python.org/dev/peps/pep-0526/ >`_ variable
265
+ annotations can be used to declare protocol attributes. However, protocols
266
+ are also supported on Python 2.7 and Python 3.3+ with the help of type
267
+ comments and properties, see
268
+ `backwards compatibility in PEP 544 <https://www.python.org/dev/peps/pep-0544/#backwards-compatibility >`_.
269
+
270
+ Recursive protocols
271
+ *******************
272
+
273
+ Protocols can be recursive and mutually recursive. This could be useful for
274
+ declaring abstract recursive collections such as trees and linked lists:
275
+
276
+ .. code-block :: python
277
+
278
+ from typing import TypeVar, Optional
279
+ from typing_extensions import Protocol
280
+
281
+ class TreeLike (Protocol ):
282
+ value: int
283
+ @ property
284
+ def left (self ) -> Optional[' TreeLike' ]: ...
285
+ @ property
286
+ def right (self ) -> Optional[' TreeLike' ]: ...
287
+
288
+ class SimpleTree :
289
+ def __init__ (self , value : int ) -> None :
290
+ self .value = value
291
+ self .left: Optional[' SimpleTree' ] = None
292
+ self .right: Optional[' SimpleTree' ] = None
293
+
294
+ root = SimpleTree(0 ) # type: TreeLike # OK
295
+
296
+ Using ``isinstance() `` with protocols
297
+ *************************************
298
+
299
+ To use a protocol class with ``isinstance() ``, one needs to decorate it with
300
+ a special ``typing_extensions.runtime `` decorator. It will add support for
301
+ basic runtime structural checks:
302
+
303
+ .. code-block :: python
304
+
305
+ from typing_extensions import Protocol, runtime
306
+
307
+ @runtime
308
+ class Portable (Protocol ):
309
+ handles: int
310
+
311
+ class Mug :
312
+ def __init__ (self ) -> None :
313
+ self .handles = 1
314
+
315
+ mug = Mug()
316
+ if isinstance (mug, Portable):
317
+ use(mug.handles) # Works statically and at runtime.
318
+
154
319
.. note ::
320
+ ``isinstance() `` is with protocols not completely safe at runtime.
321
+ For example, signatures of methods are not checked. The runtime
322
+ implementation only checks the presence of all protocol members
323
+ in object's MRO.
155
324
156
- There are also plans to support more Python-style "duck typing" in
157
- the type system. The details are still open.
0 commit comments