Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 73 additions & 1 deletion docs/source/generics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,80 @@ example we use the same type variable in two generic functions:
def last(seq: Sequence[T]) -> T:
return seq[-1]

.. generic-methods-and-generic-self:

Generic methods and generic self
********************************

You can also define generic methods — just use a type variable in the
method signature that is different from class type variables.
method signature that is different from class type variables. In particular,
``self`` may also be generic, allowing a method to return the most precise
type known at the point of access.

In this way, for example, you can typecheck chaining of setter methods:

.. code-block:: python

from typing import TypeVar

T = TypeVar('T', bound='Shape')

class Shape:
def set_scale(self: T, scale: float) -> T:
self.scale = scale
return self

class Circle(Shape):
def set_radius(self, r: float) -> 'Circle':
self.radius = r
return self

class Square(Shape):
def set_width(self, w: float) -> 'Square':
self.width = w
return self

circle = Circle().set_scale(0.5).set_radius(2.7) # type: Circle
square = Square().set_scale(0.5).set_width(3.2) # type: Square

Without using generic ``self``, the last two lines could not be type-checked properly.

Other uses are factory methods, such as copy and deserialization.
For class methods, you can also define generic ``cls``, using ``Type[T]``:

.. code-block:: python

from typing import TypeVar, Tuple, Type

T = TypeVar('T', bound='Friend')

class Friend:
other = None # type: Friend

@classmethod
def make_pair(cls: Type[T]) -> Tuple[T, T]:
a, b = cls(), cls()
a.other = b
b.other = a
return a, b

class SuperFriend(Friend):
pass

a, b = SuperFriend.make_pair()

Note that when overriding a method with generic ``self``, you must either
return a generic ``self`` too, or return an instance of the current class.
In the latter case, you must implement this method in all future subclasses.

Note also that mypy cannot always verify that a the implementation of a copy
or a deserialization method returns the actual type of self. Therefore
you may need to silence mypy inside these methods (but not at the call site),
possibly by making use of the ``Any`` type.

The behavior of the type of self is still not completely specified.
In certain cases the mypy might disallow legitimate use cases, or allow unsafe usage.


.. _type-variable-value-restriction:

Expand Down