@@ -158,30 +158,32 @@ Protocols and structural subtyping
158
158
159
159
.. note ::
160
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
161
+ Structural subtyping is experimental. Some things may not
162
+ work as expected. Mypy may pass unsafe code or it can reject
163
+ valid code.
164
+
165
+ Mypy supports two ways of deciding whether two classes are compatible
166
+ as types: nominal subtyping and structural subtyping. *Nominal *
167
+ subtyping is strictly based on the class hierarchy. If class ``D ``
168
+ inherits class ``C ``, it's also a subtype of ``C ``. This form of
169
+ subtyping is used by default in mypy, since it's easy to understand
170
+ and produces clear and concise error messages, and since it matches
171
+ how the native ``isinstance() `` check works -- based on class
172
+ hierarchy. *Structural * subtyping can also be useful. Class ``D `` is
173
+ a structural subtype of class ``C `` if the former has all attributes
174
+ and methods of the latter, and with compatible types.
175
+
176
+ Structural subtyping can be seen as a static equivalent of duck
177
+ typing, which is well known to Python programmers. Mypy provides an
178
+ opt-in support for structural subtyping via protocol classes described
179
+ below. See `PEP 544 <https://www.python.org/dev/peps/pep-0544/ >`_ for
180
+ the detailed specification of protocols and structural subtyping in
181
+ Python.
182
+
183
+ Simple user-defined protocols
184
+ *****************************
185
+
186
+ You can define a protocol class by inheriting the special
185
187
``typing_extensions.Protocol `` class:
186
188
187
189
.. code-block :: python
@@ -191,37 +193,42 @@ To define a protocol class, one must inherit the special
191
193
192
194
class SupportsClose (Protocol ):
193
195
def close (self ) -> None :
194
- ...
196
+ ... # Explicit '...'
197
+
198
+ class Resource : # No SupportsClose base class!
199
+ # ... some methods ...
195
200
196
- class Resource : # Note, this class does not have 'SupportsClose' base.
197
- # some methods
198
201
def close (self ) -> None :
199
202
self .resource.release()
200
203
201
- def close_all (things : Iterable[SupportsClose]) -> None :
202
- for thing in things :
203
- thing .close()
204
+ def close_all (items : Iterable[SupportsClose]) -> None :
205
+ for item in items :
206
+ item .close()
204
207
205
- close_all([Resource(), open (' some/file' )]) # This passes type check
208
+ close_all([Resource(), open (' some/file' )]) # Okay!
209
+
210
+ ``Resource `` is a subtype of the ``SupportClose `` protocol since it defines
211
+ a compatible ``close `` method. Regular file objects returned by ``open() `` are
212
+ similarly compatible with the protocol, as they support ``close() ``.
206
213
207
214
.. note ::
208
215
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.
216
+ The ``Protocol `` base class is currently provided in the ``typing_extensions ``
217
+ package. Once structural subtyping is mature and
218
+ `PEP 544 <https://www.python.org/dev/peps/pep-0544/ >`_ has been accepted,
219
+ ``Protocol `` will be included in the ``typing `` module. Several library
220
+ types such as ``typing.Sized `` and ``typing.Iterable `` will also be changed
221
+ into protocols. They are currently treated as regular ABCs by mypy .
215
222
216
223
Defining subprotocols
217
224
*********************
218
225
219
- Subprotocols are also supported . Existing protocols can be extended
220
- and merged using multiple inheritance. For example :
226
+ You can also define subprotocols . Existing protocols can be extended
227
+ and merged using multiple inheritance. Example :
221
228
222
229
.. code-block :: python
223
230
224
- # continuing from previous example
231
+ # ... continuing from the previous example
225
232
226
233
class SupportsRead (Protocol ):
227
234
def read (self , amount : int ) -> bytes : ...
@@ -232,46 +239,47 @@ and merged using multiple inheritance. For example:
232
239
class AdvancedResource (Resource ):
233
240
def __init__ (self , label : str ) -> None :
234
241
self .label = label
242
+
235
243
def read (self , amount : int ) -> bytes :
236
244
# some implementation
237
245
...
238
246
239
- resource = None # type: TaggedReadableResource
240
-
241
- # some code
242
-
247
+ resource: TaggedReadableResource
243
248
resource = AdvancedResource(' handle with care' ) # OK
244
249
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:
250
+ Note that inheriting from an existing protocol does not automatically
251
+ turn the subclass into a protocol -- it just creates a regular
252
+ (non-protocol) ABC that implements the given protocol (or
253
+ protocols). The ``typing_extensions.Protocol `` base class must always
254
+ be explicitly present if you are defining a protocol:
249
255
250
256
.. code-block :: python
251
257
252
258
class NewProtocol (SupportsClose ): # This is NOT a protocol
253
259
new_attr: int
254
260
255
261
class Concrete :
256
- new_attr = None # type: int
262
+ new_attr: int = 0
263
+
257
264
def close (self ) -> None :
258
265
...
259
- # Below is an error, since nominal subtyping is used by default
260
- x = Concrete() # type: NewProtocol # Error!
266
+
267
+ # Error: nominal subtyping used by default
268
+ x: NewProtocol = Concrete() # Error!
261
269
262
270
.. note ::
263
271
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 >`_.
272
+ You can use Python 3.6 variable annotations (`PEP 526
273
+ <https://www.python.org/dev/peps/pep-0526/> `_)
274
+ to declare protocol attributes. On Python 2.7 and earlier Python 3
275
+ versions you can use type comments and properties.
269
276
270
277
Recursive protocols
271
278
*******************
272
279
273
- Protocols can be recursive and mutually recursive. This could be useful for
274
- declaring abstract recursive collections such as trees and linked lists:
280
+ Protocols can be recursive (self-referential) and mutually
281
+ recursive. This is useful for declaring abstract recursive collections
282
+ such as trees and linked lists:
275
283
276
284
.. code-block :: python
277
285
@@ -280,8 +288,10 @@ declaring abstract recursive collections such as trees and linked lists:
280
288
281
289
class TreeLike (Protocol ):
282
290
value: int
291
+
283
292
@ property
284
293
def left (self ) -> Optional[' TreeLike' ]: ...
294
+
285
295
@ property
286
296
def right (self ) -> Optional[' TreeLike' ]: ...
287
297
@@ -296,9 +306,9 @@ declaring abstract recursive collections such as trees and linked lists:
296
306
Using ``isinstance() `` with protocols
297
307
*************************************
298
308
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:
309
+ You can use a protocol class with ``isinstance() `` if you decorate it
310
+ with the ``typing_extensions.runtime `` class decorator. The decorator
311
+ adds support for basic runtime structural checks:
302
312
303
313
.. code-block :: python
304
314
@@ -314,11 +324,9 @@ basic runtime structural checks:
314
324
315
325
mug = Mug()
316
326
if isinstance (mug, Portable):
317
- use(mug.handles) # Works statically and at runtime.
327
+ use(mug.handles) # Works statically and at runtime
318
328
319
329
.. note ::
320
- ``isinstance() `` is with protocols not completely safe at runtime.
330
+ ``isinstance() `` with protocols is not completely safe at runtime.
321
331
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.
324
-
332
+ implementation only checks that all protocol members are defined.
0 commit comments