@@ -4,122 +4,6 @@ Additional features
44This section discusses various features that did not fit in naturally in one
55of the previous sections.
66
7- .. _function-overloading :
8-
9- Function overloading
10- ********************
11-
12- Sometimes the types in a function depend on each other in ways that
13- can't be captured with a ``Union ``. For example, the ``__getitem__ ``
14- (``[] `` bracket indexing) method can take an integer and return a
15- single item, or take a ``slice `` and return a ``Sequence `` of items.
16- You might be tempted to annotate it like so:
17-
18- .. code-block :: python
19-
20- from typing import Sequence, TypeVar, Union
21- T = TypeVar(' T' )
22-
23- class MyList (Sequence[T]):
24- def __getitem__ (self , index : Union[int , slice ]) -> Union[T, Sequence[T]]:
25- if isinstance (index, int ):
26- ... # Return a T here
27- elif isinstance (index, slice ):
28- ... # Return a sequence of Ts here
29- else :
30- raise TypeError (... )
31-
32- But this is too loose, as it implies that when you pass in an ``int ``
33- you might sometimes get out a single item and sometimes a sequence.
34- The return type depends on the parameter type in a way that can't be
35- expressed using a type variable. Instead, we can use `overloading
36- <https://www.python.org/dev/peps/pep-0484/#function-method-overloading> `_
37- to give the same function multiple type annotations (signatures) and
38- accurately describe the function's behavior.
39-
40- .. code-block :: python
41-
42- from typing import overload, Sequence, TypeVar, Union
43- T = TypeVar(' T' )
44-
45- class MyList (Sequence[T]):
46-
47- # The @overload definitions are just for the type checker,
48- # and overwritten by the real implementation below.
49- @overload
50- def __getitem__ (self , index : int ) -> T:
51- pass # Don't put code here
52-
53- # All overloads and the implementation must be adjacent
54- # in the source file, and overload order may matter:
55- # when two overloads may overlap, the more specific one
56- # should come first.
57- @overload
58- def __getitem__ (self , index : slice ) -> Sequence[T]:
59- pass # Don't put code here
60-
61- # The implementation goes last, without @overload.
62- # It may or may not have type hints; if it does,
63- # these are checked against the overload definitions
64- # as well as against the implementation body.
65- def __getitem__ (self , index : Union[int , slice ]) -> Union[T, Sequence[T]]:
66- # This is exactly the same as before.
67- if isinstance (index, int ):
68- ... # Return a T here
69- elif isinstance (index, slice ):
70- ... # Return a sequence of Ts here
71- else :
72- raise TypeError (... )
73-
74- Calls to overloaded functions are type checked against the variants,
75- not against the implementation. A call like ``my_list[5] `` would have
76- type ``T ``, not ``Union[T, Sequence[T]] `` because it matches the
77- first overloaded definition, and ignores the type annotations on the
78- implementation of ``__getitem__ ``. The code in the body of the
79- definition of ``__getitem__ `` is checked against the annotations on
80- the corresponding declaration. In this case the body is checked
81- with ``index: Union[int, slice] `` and a return type
82- ``Union[T, Sequence[T]] ``. If there are no annotations on the
83- corresponding definition, then code in the function body is not type
84- checked.
85-
86- The annotations on the function body must be compatible with the
87- types given for the overloaded variants listed above it. The type
88- checker will verify that all the types for the overloaded variants
89- are compatible with the types given for the implementation. In this
90- case it checks that the parameter type ``int `` and the return type
91- ``T `` are compatible with ``Union[int, slice] `` and
92- ``Union[T, Sequence[T]] `` for the first variant. For the second
93- variant it verifies that the parameter type ``slice `` and the return
94- type ``Sequence[T] `` are compatible with ``Union[int, slice] `` and
95- ``Union[T, Sequence[T]] ``.
96-
97- Overloaded function variants are still ordinary Python functions and
98- they still define a single runtime object. There is no automatic
99- dispatch happening, and you must manually handle the different types
100- in the implementation (usually with :func: `isinstance ` checks, as
101- shown in the example).
102-
103- The overload variants must be adjacent in the code. This makes code
104- clearer, as you don't have to hunt for overload variants across the
105- file.
106-
107- Overloads in stub files are exactly the same, except there is no
108- implementation.
109-
110- .. note ::
111-
112- As generic type variables are erased at runtime when constructing
113- instances of generic types, an overloaded function cannot have
114- variants that only differ in a generic type argument,
115- e.g. ``List[int] `` and ``List[str] ``.
116-
117- .. note ::
118-
119- If you just need to constrain a type variable to certain types or
120- subtypes, you can use a :ref: `value restriction
121- <type-variable-value-restriction>`.
122-
1237.. _attrs_package :
1248
1259The attrs package
0 commit comments