@@ -4,122 +4,6 @@ Additional features
4
4
This section discusses various features that did not fit in naturally in one
5
5
of the previous sections.
6
6
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
-
123
7
.. _attrs_package :
124
8
125
9
The attrs package
0 commit comments