diff --git a/docs/source/class_basics.rst b/docs/source/class_basics.rst
index e03c299e977c..3dc116cfb089 100644
--- a/docs/source/class_basics.rst
+++ b/docs/source/class_basics.rst
@@ -158,30 +158,32 @@ Protocols and structural subtyping
.. note::
- The support for structural subtyping is still experimental. Some features
- might be not yet implemented, mypy could pass unsafe code or reject
- working code.
-
-There are two main type systems with respect to subtyping: nominal subtyping
-and structural subtyping. The *nominal* subtyping is based on class hierarchy,
-so that if class ``D`` inherits from class ``C``, then it is a subtype
-of ``C``. This type system is primarily used in mypy since it allows
-to produce clear and concise error messages, and since Python provides native
-``isinstance()`` checks based on class hierarchy. The *structural* subtyping
-however has its own advantages. In this system class ``D`` is a subtype
-of class ``C`` if the former has all attributes of the latter with
-compatible types.
-
-This type system is a static equivalent of duck typing, well known by Python
-programmers. Mypy provides an opt-in support for structural subtyping via
-protocol classes described in this section.
-See `PEP 544 `_ for
-specification of protocols and structural subtyping in Python.
-
-User defined protocols
-**********************
-
-To define a protocol class, one must inherit the special
+ Structural subtyping is experimental. Some things may not
+ work as expected. Mypy may pass unsafe code or it can reject
+ valid code.
+
+Mypy supports two ways of deciding whether two classes are compatible
+as types: nominal subtyping and structural subtyping. *Nominal*
+subtyping is strictly based on the class hierarchy. If class ``D``
+inherits class ``C``, it's also a subtype of ``C``. This form of
+subtyping is used by default in mypy, since it's easy to understand
+and produces clear and concise error messages, and since it matches
+how the native ``isinstance()`` check works -- based on class
+hierarchy. *Structural* subtyping can also be useful. Class ``D`` is
+a structural subtype of class ``C`` if the former has all attributes
+and methods of the latter, and with compatible types.
+
+Structural subtyping can be seen as a static equivalent of duck
+typing, which is well known to Python programmers. Mypy provides an
+opt-in support for structural subtyping via protocol classes described
+below. See `PEP 544 `_ for
+the detailed specification of protocols and structural subtyping in
+Python.
+
+Simple user-defined protocols
+*****************************
+
+You can define a protocol class by inheriting the special
``typing_extensions.Protocol`` class:
.. code-block:: python
@@ -191,37 +193,42 @@ To define a protocol class, one must inherit the special
class SupportsClose(Protocol):
def close(self) -> None:
- ...
+ ... # Explicit '...'
+
+ class Resource: # No SupportsClose base class!
+ # ... some methods ...
- class Resource: # Note, this class does not have 'SupportsClose' base.
- # some methods
def close(self) -> None:
self.resource.release()
- def close_all(things: Iterable[SupportsClose]) -> None:
- for thing in things:
- thing.close()
+ def close_all(items: Iterable[SupportsClose]) -> None:
+ for item in items:
+ item.close()
- close_all([Resource(), open('some/file')]) # This passes type check
+ close_all([Resource(), open('some/file')]) # Okay!
+
+``Resource`` is a subtype of the ``SupportClose`` protocol since it defines
+a compatible ``close`` method. Regular file objects returned by ``open()`` are
+similarly compatible with the protocol, as they support ``close()``.
.. note::
- The ``Protocol`` base class is currently provided in ``typing_extensions``
- package. When structural subtyping is mature and
- `PEP 544 `_ is accepted,
- ``Protocol`` will be included in the ``typing`` module. As well, several
- types such as ``typing.Sized``, ``typing.Iterable`` etc. will be made
- protocols.
+ The ``Protocol`` base class is currently provided in the ``typing_extensions``
+ package. Once structural subtyping is mature and
+ `PEP 544 `_ has been accepted,
+ ``Protocol`` will be included in the ``typing`` module. Several library
+ types such as ``typing.Sized`` and ``typing.Iterable`` will also be changed
+ into protocols. They are currently treated as regular ABCs by mypy.
Defining subprotocols
*********************
-Subprotocols are also supported. Existing protocols can be extended
-and merged using multiple inheritance. For example:
+You can also define subprotocols. Existing protocols can be extended
+and merged using multiple inheritance. Example:
.. code-block:: python
- # continuing from previous example
+ # ... continuing from the previous example
class SupportsRead(Protocol):
def read(self, amount: int) -> bytes: ...
@@ -232,20 +239,19 @@ and merged using multiple inheritance. For example:
class AdvancedResource(Resource):
def __init__(self, label: str) -> None:
self.label = label
+
def read(self, amount: int) -> bytes:
# some implementation
...
- resource = None # type: TaggedReadableResource
-
- # some code
-
+ resource: TaggedReadableResource
resource = AdvancedResource('handle with care') # OK
-Note that inheriting from existing protocols does not automatically turn
-a subclass into a protocol, it just creates a usual (non-protocol) ABC that
-implements given protocols. The ``typing_extensions.Protocol`` base must always
-be explicitly present:
+Note that inheriting from an existing protocol does not automatically
+turn the subclass into a protocol -- it just creates a regular
+(non-protocol) ABC that implements the given protocol (or
+protocols). The ``typing_extensions.Protocol`` base class must always
+be explicitly present if you are defining a protocol:
.. code-block:: python
@@ -253,25 +259,27 @@ be explicitly present:
new_attr: int
class Concrete:
- new_attr = None # type: int
+ new_attr: int = 0
+
def close(self) -> None:
...
- # Below is an error, since nominal subtyping is used by default
- x = Concrete() # type: NewProtocol # Error!
+
+ # Error: nominal subtyping used by default
+ x: NewProtocol = Concrete() # Error!
.. note::
- The `PEP 526 `_ variable
- annotations can be used to declare protocol attributes. However, protocols
- are also supported on Python 2.7 and Python 3.3+ with the help of type
- comments and properties, see
- `backwards compatibility in PEP 544 `_.
+ You can use Python 3.6 variable annotations (`PEP 526
+ `_)
+ to declare protocol attributes. On Python 2.7 and earlier Python 3
+ versions you can use type comments and properties.
Recursive protocols
*******************
-Protocols can be recursive and mutually recursive. This could be useful for
-declaring abstract recursive collections such as trees and linked lists:
+Protocols can be recursive (self-referential) and mutually
+recursive. This is useful for declaring abstract recursive collections
+such as trees and linked lists:
.. code-block:: python
@@ -280,8 +288,10 @@ declaring abstract recursive collections such as trees and linked lists:
class TreeLike(Protocol):
value: int
+
@property
def left(self) -> Optional['TreeLike']: ...
+
@property
def right(self) -> Optional['TreeLike']: ...
@@ -296,9 +306,9 @@ declaring abstract recursive collections such as trees and linked lists:
Using ``isinstance()`` with protocols
*************************************
-To use a protocol class with ``isinstance()``, one needs to decorate it with
-a special ``typing_extensions.runtime`` decorator. It will add support for
-basic runtime structural checks:
+You can use a protocol class with ``isinstance()`` if you decorate it
+with the ``typing_extensions.runtime`` class decorator. The decorator
+adds support for basic runtime structural checks:
.. code-block:: python
@@ -314,11 +324,9 @@ basic runtime structural checks:
mug = Mug()
if isinstance(mug, Portable):
- use(mug.handles) # Works statically and at runtime.
+ use(mug.handles) # Works statically and at runtime
.. note::
- ``isinstance()`` is with protocols not completely safe at runtime.
+ ``isinstance()`` with protocols is not completely safe at runtime.
For example, signatures of methods are not checked. The runtime
- implementation only checks the presence of all protocol members
- in object's MRO.
-
+ implementation only checks that all protocol members are defined.
diff --git a/docs/source/generics.rst b/docs/source/generics.rst
index 8f8f5355b93e..2ea9b424e139 100644
--- a/docs/source/generics.rst
+++ b/docs/source/generics.rst
@@ -328,9 +328,9 @@ Let us illustrate this by few simple examples:
def salaries(staff: List[Manager],
accountant: Callable[[Manager], int]) -> List[int]: ...
- this function needs a callable that can calculate a salary for managers, and
+ This function needs a callable that can calculate a salary for managers, and
if we give it a callable that can calculate a salary for an arbitrary
- employee, then it is still safe.
+ employee, it's still safe.
* ``List`` is an invariant generic type. Naively, one would think
that it is covariant, but let us consider this code:
@@ -338,6 +338,7 @@ Let us illustrate this by few simple examples:
class Shape:
pass
+
class Circle(Shape):
def rotate(self):
...
@@ -349,7 +350,7 @@ Let us illustrate this by few simple examples:
add_one(my_things) # This may appear safe, but...
my_things[0].rotate() # ...this will fail
- Another example of invariant type is ``Dict``, most mutable containers
+ Another example of invariant type is ``Dict``. Most mutable containers
are invariant.
By default, mypy assumes that all user-defined generics are invariant.
@@ -360,15 +361,18 @@ type variables defined with special keyword arguments ``covariant`` or
.. code-block:: python
from typing import Generic, TypeVar
+
T_co = TypeVar('T_co', covariant=True)
class Box(Generic[T_co]): # this type is declared covariant
def __init__(self, content: T_co) -> None:
self._content = content
+
def get_content(self) -> T_co:
return self._content
def look_into(box: Box[Animal]): ...
+
my_box = Box(Cat())
look_into(my_box) # OK, but mypy would complain here for an invariant type
@@ -491,73 +495,6 @@ restrict the valid values for the type parameter in the same way.
A type variable may not have both a value restriction (see
:ref:`type-variable-value-restriction`) and an upper bound.
-Generic protocols
-*****************
-
-Generic protocols (see :ref:`protocol-types`) are also supported, generic
-protocols mostly follow the normal rules for generic classes, the main
-difference is that mypy checks that declared variance of type variables is
-compatible with the class definition. Examples:
-
-.. code-block:: python
-
- from typing import TypeVar
- from typing_extensions import Protocol
-
- T = TypeVar('T')
-
- class Box(Protocol[T]):
- content: T
-
- def do_stuff(one: Box[str], other: Box[bytes]) -> None:
- ...
-
- class StringWrapper:
- def __init__(self, content: str) -> None:
- self.content = content
-
- class BytesWrapper:
- def __init__(self, content: bytes) -> None:
- self.content = content
-
- do_stuff(StringWrapper('one'), BytesWrapper(b'other')) # OK
-
- x = None # type: Box[float]
- y = None # type: Box[int]
- x = y # Error, since the protocol 'Box' is invariant.
-
- class AnotherBox(Protocol[T]): # Error, covariant type variable expected
- def content(self) -> T:
- ...
-
- T_co = TypeVar('T_co', covariant=True)
- class AnotherBox(Protocol[T_co]): # OK
- def content(self) -> T_co:
- ...
-
- ax = None # type: AnotherBox[float]
- ay = None # type: AnotherBox[int]
- ax = ay # OK for covariant protocols
-
-See :ref:`variance-of-generics` above for more details on variance.
-Generic protocols can be recursive, for example:
-
-.. code-block:: python
-
- T = TypeVar('T')
- class Linked(Protocol[T]):
- val: T
- def next(self) -> 'Linked[T]': ...
-
- class L:
- val: int
- def next(self) -> 'L': ...
-
- def last(seq: Linked[T]) -> T:
- ...
-
- result = last(L()) # The inferred type of 'result' is 'int'
-
.. _declaring-decorators:
Declaring decorators
@@ -608,3 +545,94 @@ Also note that the ``wrapper()`` function is not type-checked. Wrapper
functions are typically small enough that this is not a big
problem. This is also the reason for the ``cast()`` call in the
``return`` statement in ``my_decorator()``. See :ref:`casts`.
+
+Generic protocols
+*****************
+
+Mypy supports generic protocols (see also :ref:`protocol-types`). Generic
+protocols mostly follow the normal rules for generic classes. Example:
+
+.. code-block:: python
+
+ from typing import TypeVar
+ from typing_extensions import Protocol
+
+ T = TypeVar('T')
+
+ class Box(Protocol[T]):
+ content: T
+
+ def do_stuff(one: Box[str], other: Box[bytes]) -> None:
+ ...
+
+ class StringWrapper:
+ def __init__(self, content: str) -> None:
+ self.content = content
+
+ class BytesWrapper:
+ def __init__(self, content: bytes) -> None:
+ self.content = content
+
+ do_stuff(StringWrapper('one'), BytesWrapper(b'other')) # OK
+
+ x: Box[float] = ...
+ y: Box[int] = ...
+ x = y # Error -- Box is invariant
+
+The main difference between generic protocols and ordinary generic
+classes is that mypy checks that the declared variances of generic
+type variables in a protocol match how they are used in the protocol
+definition. The protocol in this example is rejected, since the type
+variable ``T`` is used covariantly as a return type, but the type
+variable is invariant:
+
+.. code-block:: python
+
+ from typing import TypeVar
+ from typing_extensions import Protocol
+
+ T = TypeVar('T')
+
+ class ReadOnlyBox(Protocol[T]): # Error: covariant type variable expected
+ def content(self) -> T: ...
+
+This example correctly uses a covariant type variable:
+
+.. code-block:: python
+
+ from typing import TypeVar
+ from typing_extensions import Protocol
+
+ T_co = TypeVar('T_co', covariant=True)
+
+ class ReadOnlyBox(Protocol[T_co]): # OK
+ def content(self) -> T_co: ...
+
+ ax: ReadOnlyBox[float] = ...
+ ay: ReadOnlyBox[int] = ...
+ ax = ay # OK -- ReadOnlyBox is covariant
+
+See :ref:`variance-of-generics` for more about variance.
+
+Generic protocols can also be recursive. Example:
+
+.. code-block:: python
+
+ T = TypeVar('T')
+
+ class Linked(Protocol[T]):
+ val: T
+ def next(self) -> 'Linked[T]': ...
+
+ class L:
+ val: int
+
+ ... # details omitted
+
+ def next(self) -> 'L':
+ ... # details omitted
+
+ def last(seq: Linked[T]) -> T:
+ ... # implementation omitted
+
+ result = last(L()) # Inferred type of 'result' is 'int'
diff --git a/docs/source/type_inference_and_annotations.rst b/docs/source/type_inference_and_annotations.rst
index 76e9cf9d9014..2ae6396c2d5e 100644
--- a/docs/source/type_inference_and_annotations.rst
+++ b/docs/source/type_inference_and_annotations.rst
@@ -78,6 +78,22 @@ type:
x = 1.1 # type: Union[int, str] # Error!
+Python 3.6 introduced a new syntax for variable annotations, which
+resembles function annotations:
+
+.. code-block:: python
+
+ x: Union[int, str] = 1
+
+We'll use both syntax variants in examples. The syntax variants are
+mostly interchangeable, but the Python 3.6 syntax allows defining the
+type of a variable without initialization, which is not possible with
+the comment-based syntax:
+
+.. code-block:: python
+
+ x: str # Declare type of 'x' without initialization
+
.. note::
The best way to think about this is that the type comment sets the