From 82d6405782707deb33488ed61b400526d7bbd530 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Mon, 14 Nov 2016 20:10:30 +0200 Subject: [PATCH 1/2] Documenting generic self --- docs/source/generics.rst | 74 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 346ea0f17006..7f6b639450f3 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -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: From ac340b6fa9cb3fc611c76003347fe84152df435f Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Tue, 15 Nov 2016 00:45:13 +0200 Subject: [PATCH 2/2] typo --- docs/source/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 7f6b639450f3..c62742a43642 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -211,7 +211,7 @@ 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. +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.