Skip to content

Commit 1f9e139

Browse files
JukkaLgvanrossum
authored andcommitted
Updates to documentation of type annotations and protocols (#4050)
Introduce Python 3.6 variable annotations early on since they are used in some examples. In the future we can use both variable annotations and type comments in examples. Various tweaks to protocols documentation. Mostly make it more consistent in style with the rest of the documentation.
1 parent 4e6778c commit 1f9e139

File tree

3 files changed

+188
-136
lines changed

3 files changed

+188
-136
lines changed

docs/source/class_basics.rst

Lines changed: 74 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -158,30 +158,32 @@ Protocols and structural subtyping
158158

159159
.. note::
160160

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
185187
``typing_extensions.Protocol`` class:
186188

187189
.. code-block:: python
@@ -191,37 +193,42 @@ To define a protocol class, one must inherit the special
191193
192194
class SupportsClose(Protocol):
193195
def close(self) -> None:
194-
...
196+
... # Explicit '...'
197+
198+
class Resource: # No SupportsClose base class!
199+
# ... some methods ...
195200
196-
class Resource: # Note, this class does not have 'SupportsClose' base.
197-
# some methods
198201
def close(self) -> None:
199202
self.resource.release()
200203
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()
204207
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()``.
206213

207214
.. note::
208215

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.
215222

216223
Defining subprotocols
217224
*********************
218225

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:
221228

222229
.. code-block:: python
223230
224-
# continuing from previous example
231+
# ... continuing from the previous example
225232
226233
class SupportsRead(Protocol):
227234
def read(self, amount: int) -> bytes: ...
@@ -232,46 +239,47 @@ and merged using multiple inheritance. For example:
232239
class AdvancedResource(Resource):
233240
def __init__(self, label: str) -> None:
234241
self.label = label
242+
235243
def read(self, amount: int) -> bytes:
236244
# some implementation
237245
...
238246
239-
resource = None # type: TaggedReadableResource
240-
241-
# some code
242-
247+
resource: TaggedReadableResource
243248
resource = AdvancedResource('handle with care') # OK
244249
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:
249255

250256
.. code-block:: python
251257
252258
class NewProtocol(SupportsClose): # This is NOT a protocol
253259
new_attr: int
254260
255261
class Concrete:
256-
new_attr = None # type: int
262+
new_attr: int = 0
263+
257264
def close(self) -> None:
258265
...
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!
261269
262270
.. note::
263271

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.
269276

270277
Recursive protocols
271278
*******************
272279

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:
275283

276284
.. code-block:: python
277285
@@ -280,8 +288,10 @@ declaring abstract recursive collections such as trees and linked lists:
280288
281289
class TreeLike(Protocol):
282290
value: int
291+
283292
@property
284293
def left(self) -> Optional['TreeLike']: ...
294+
285295
@property
286296
def right(self) -> Optional['TreeLike']: ...
287297
@@ -296,9 +306,9 @@ declaring abstract recursive collections such as trees and linked lists:
296306
Using ``isinstance()`` with protocols
297307
*************************************
298308

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:
302312

303313
.. code-block:: python
304314
@@ -314,11 +324,9 @@ basic runtime structural checks:
314324
315325
mug = Mug()
316326
if isinstance(mug, Portable):
317-
use(mug.handles) # Works statically and at runtime.
327+
use(mug.handles) # Works statically and at runtime
318328
319329
.. note::
320-
``isinstance()`` is with protocols not completely safe at runtime.
330+
``isinstance()`` with protocols is not completely safe at runtime.
321331
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

Comments
 (0)