From de452ea21c4e238fa46b3f3fc24c6c50d9327248 Mon Sep 17 00:00:00 2001 From: jkleint Date: Wed, 1 Feb 2017 22:20:22 -0800 Subject: [PATCH] Show how to @overload function annotations in user code The docs say `@override` doesn't work in user code, but it seems to work in mypy 0.470. The update may be waiting on #2603, but that PR does not seem to include doc updates, so feel free to put this patch in that PR. --- docs/source/function_overloading.rst | 75 +++++++++++++++------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/docs/source/function_overloading.rst b/docs/source/function_overloading.rst index b55cddd43fc2..9f907260ba40 100644 --- a/docs/source/function_overloading.rst +++ b/docs/source/function_overloading.rst @@ -1,60 +1,63 @@ -Function overloading in stubs -============================= +Function Overloading +==================== -Sometimes you have a library function that seems to call for two or -more signatures. That's okay -- you can define multiple *overloaded* -instances of a function with the same name but different signatures in -a stub file (this feature is not supported for user code, at least not -yet) using the ``@overload`` decorator. For example, we can define an -``abs`` function that works for both ``int`` and ``float`` arguments: +Sometimes the types in a function depend on each other in ways that can't be +captured with a simple ``Union``. For example, the ``__getitem__`` (``[]`` bracket +indexing) method can take an integer and return a single item, or take a ``slice`` +and return a ``Sequence`` of items. You might be tempted to annotate it like so: .. code-block:: python - # This is a stub file! + class Seq(Generic[T], Sequence[T]): + def __getitem__(self, index: Union[int, slice]) -> Union[T, Sequence[T]]: + pass + +But this is a little loose, as it implies that when you put in an ``int`` you might +sometimes get out a single item or sometimes a sequence. To capture a constraint +such as a return type that depends on a parameter type, we can use +`overloading `_ +to give the same function multiple type annotations (signatures). - from typing import overload - - @overload - def abs(n: int) -> int: pass - - @overload - def abs(n: float) -> float: pass +.. code-block:: python -Note that we can't use ``Union[int, float]`` as the argument type, -since this wouldn't allow us to express that the return -type depends on the argument type. + from typing import Generic, Sequence, overload + T = TypeVar('T') -Now if we import ``abs`` as defined in the above library stub, we can -write code like this, and the types are inferred correctly: + class Seq(Generic[T], Sequence[T]): + @overload # These are just for the type checker, and overwritten by the real implementation + def __getitem__(self, index: int) -> T: + pass -.. code-block:: python + @overload # All overloads and the implementation must be adjacent in the source file, and overload order may matter + def __getitem__(self, index: slice) -> Sequence[T]: + pass - n = abs(-2) # 2 (int) - f = abs(-1.5) # 1.5 (float) + def __getitem__(self, index): # Actual implementation goes last, and does *not* get type hints or @overload decorator + if isinstance(index, int): + ... + elif isinstance(index, slice): + ... Overloaded function variants are still ordinary Python functions and -they still define a single runtime object. The following code is -thus valid: - -.. code-block:: python - - my_abs = abs - my_abs(-2) # 2 (int) - my_abs(-1.5) # 1.5 (float) +they still define a single runtime object. There is no multiple dispatch +happening, and you must manually handle the different types (usually with +:func:`isinstance` checks). The overload variants must be adjacent in the code. This makes code clearer, as you don't have to hunt for overload variants across the file. +Overloads in stub files are exactly the same, except of course there is no +implementation. + .. note:: As generic type variables are erased at runtime when constructing instances of generic types, an overloaded function cannot have variants that only differ in a generic type argument, - e.g. ``List[int]`` versus ``List[str]``. + e.g. ``List[int]`` and ``List[str]``. .. note:: - If you are writing a regular module rather than a stub, you can - often use a type variable with a value restriction to represent - functions as ``abs`` above (see :ref:`type-variable-value-restriction`). + If you just need to constrain a type variable to certain types or subtypes, + you can use a :ref:`value restriction `).