Skip to content

Commit 7c9d8aa

Browse files
jkleintGuido van Rossum
authored and
Guido van Rossum
committed
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.
1 parent 95c30f5 commit 7c9d8aa

File tree

1 file changed

+39
-36
lines changed

1 file changed

+39
-36
lines changed

docs/source/function_overloading.rst

+39-36
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,63 @@
1-
Function overloading in stubs
2-
=============================
1+
Function Overloading
2+
====================
33

4-
Sometimes you have a library function that seems to call for two or
5-
more signatures. That's okay -- you can define multiple *overloaded*
6-
instances of a function with the same name but different signatures in
7-
a stub file (this feature is not supported for user code, at least not
8-
yet) using the ``@overload`` decorator. For example, we can define an
9-
``abs`` function that works for both ``int`` and ``float`` arguments:
4+
Sometimes the types in a function depend on each other in ways that can't be
5+
captured with a simple ``Union``. For example, the ``__getitem__`` (``[]`` bracket
6+
indexing) method can take an integer and return a single item, or take a ``slice``
7+
and return a ``Sequence`` of items. You might be tempted to annotate it like so:
108

119
.. code-block:: python
1210
13-
# This is a stub file!
11+
class Seq(Generic[T], Sequence[T]):
12+
def __getitem__(self, index: Union[int, slice]) -> Union[T, Sequence[T]]:
13+
pass
14+
15+
But this is a little loose, as it implies that when you put in an ``int`` you might
16+
sometimes get out a single item or sometimes a sequence. To capture a constraint
17+
such as a return type that depends on a parameter type, we can use
18+
`overloading <https://www.python.org/dev/peps/pep-0484/#function-method-overloading>`_
19+
to give the same function multiple type annotations (signatures).
1420

15-
from typing import overload
16-
17-
@overload
18-
def abs(n: int) -> int: pass
19-
20-
@overload
21-
def abs(n: float) -> float: pass
21+
.. code-block:: python
2222
23-
Note that we can't use ``Union[int, float]`` as the argument type,
24-
since this wouldn't allow us to express that the return
25-
type depends on the argument type.
23+
from typing import Generic, Sequence, overload
24+
T = TypeVar('T')
2625
27-
Now if we import ``abs`` as defined in the above library stub, we can
28-
write code like this, and the types are inferred correctly:
26+
class Seq(Generic[T], Sequence[T]):
27+
@overload # These are just for the type checker, and overwritten by the real implementation
28+
def __getitem__(self, index: int) -> T:
29+
pass
2930
30-
.. code-block:: python
31+
@overload # All overloads and the implementation must be adjacent in the source file, and overload order may matter
32+
def __getitem__(self, index: slice) -> Sequence[T]:
33+
pass
3134
32-
n = abs(-2) # 2 (int)
33-
f = abs(-1.5) # 1.5 (float)
35+
def __getitem__(self, index): # Actual implementation goes last, and does *not* get type hints or @overload decorator
36+
if isinstance(index, int):
37+
...
38+
elif isinstance(index, slice):
39+
...
3440
3541
Overloaded function variants are still ordinary Python functions and
36-
they still define a single runtime object. The following code is
37-
thus valid:
38-
39-
.. code-block:: python
40-
41-
my_abs = abs
42-
my_abs(-2) # 2 (int)
43-
my_abs(-1.5) # 1.5 (float)
42+
they still define a single runtime object. There is no multiple dispatch
43+
happening, and you must manually handle the different types (usually with
44+
:func:`isinstance` checks).
4445

4546
The overload variants must be adjacent in the code. This makes code
4647
clearer, as you don't have to hunt for overload variants across the
4748
file.
4849

50+
Overloads in stub files are exactly the same, except of course there is no
51+
implementation.
52+
4953
.. note::
5054

5155
As generic type variables are erased at runtime when constructing
5256
instances of generic types, an overloaded function cannot have
5357
variants that only differ in a generic type argument,
54-
e.g. ``List[int]`` versus ``List[str]``.
58+
e.g. ``List[int]`` and ``List[str]``.
5559

5660
.. note::
5761

58-
If you are writing a regular module rather than a stub, you can
59-
often use a type variable with a value restriction to represent
60-
functions as ``abs`` above (see :ref:`type-variable-value-restriction`).
62+
If you just need to constrain a type variable to certain types or subtypes,
63+
you can use a :ref:`value restriction <type-variable-value-restriction>`).

0 commit comments

Comments
 (0)