8.10. Function Type Annotation¶
8.10.1. Rationale¶
Before Python 3.9 you need
from typing import List, Set, Tuple, Dict
Since Python 3.9: PEP 585 -- Type Hinting Generics In Standard Collections
8.10.2. Return¶
>>> def say_hello() -> str:
... return 'My name... José Jiménez'
8.10.3. Parameters¶
>>> def add_numbers(a: int, b: int) -> int:
... return a + b
8.10.4. Union¶
>>> from typing import Union
>>>
>>>
>>> def add_numbers(a: Union[int,float], b: Union[int,float]) -> Union[int,float]:
... return a + b
>>> from typing import Union
>>>
>>> Number = Union[int, float]
>>>
>>> def add_numbers(a: Number, b: Number) -> Number:
... return a + b
Since Python 3.10: PEP 604 -- Allow writing union types as X | Y
>>> # doctest: +SKIP
... def add_numbers(a: int|float, b: int|float) -> int|float:
... return a + b
8.10.5. Optional¶
>>> from typing import Union
>>>
>>>
>>> def find(text: str, what: str) -> Union[int, None]:
... position = text.find(what)
... if position == -1:
... return None
... else:
... return position
>>>
>>>
>>> find('Python', 'x')
>>> find('Python', 'o')
4
>>> from typing import Optional
>>>
>>>
>>> def find(text: str, what: str) -> Optional[int]:
... position = text.find(what)
... if position == -1:
... return None
... else:
... return position
>>>
>>>
>>> find('Python', 'x')
>>> find('Python', 'o')
4
Since Python 3.10: PEP 604 -- Allow writing union types as X | Y
>>> # doctest: +SKIP
... def find(text: str, what: str) -> int|None:
... position = text.find(what)
... if position == -1:
... return None
... else:
... return position
>>>
>>>
>>> find('Python', 'x')
>>> find('Python', 'o')
4
Since Python 3.10: PEP 645 -- Allow writing optional types as x?
>>> # doctest: +SKIP
... def find(text: str, what: str) -> int?:
... position = text.find(what)
... if position == -1:
... return None
... else:
... return position
>>>
>>>
>>> find('Python', 'x')
>>> find('Python', 'o')
4
8.10.6. NoReturn¶
>>> from typing import NoReturn
>>>
>>>
>>> def stop() -> NoReturn:
... raise RuntimeError
>>> from typing import Union, NoReturn
>>>
>>>
>>> def valid_email(email: str) -> Union[NoReturn, str]:
... if '@' in email:
... return email
... else:
... raise ValueError('Invalid Email')
>>>
>>>
>>> valid_email('mark.watney@nasa.gov')
'mark.watney@nasa.gov'
>>>
>>> valid_email('mark.watney_at_nasa.gov')
Traceback (most recent call last):
ValueError: Invalid Email
8.10.7. Literal¶
Since Python 3.8: PEP 586 -- Literal Types
>>> from typing import Literal
>>>
>>>
>>> def open(filename: str, mode: Literal['r','w','a']) -> None:
... pass
>>>
>>> open('data.csv', mode='w') # mypy: OK
>>> open('data.csv', mode='r') # mypy: OK
>>> open('data.csv', mode='a') # mypy: OK
>>> open('data.csv', mode='x') # mypy: ERROR
8.10.8. Annotations¶
>>> def add(a: int, b: int) -> int:
... return a + b
>>>
>>>
>>> add.__annotations__
{'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
Since Python 3.10: PEP 563 -- Postponed Evaluation of Annotations
>>> def add(a: int, b: int) -> int:
... return a + b
>>>
>>> # doctest: +SKIP
... add.__annotations__
{'a': 'int', 'b': 'int', 'return': 'int'}
8.10.9. Errors¶
Python will execute without even warning
Your IDE and
mypy
et. al. will yield errors
>>> def add_numbers(a: int, b: int) -> int:
... return a + b
>>>
>>>
>>> add_numbers('Mark', 'Watney')
'MarkWatney'
8.10.10. Good Engineering Practices¶
>>> from typing import Union
>>>
>>>
>>> def add_numbers(a: Union[int,float],
... b: Union[int,float]
... ) -> Union[int,float]:
... return a + b
>>>
>>>
>>> add_numbers(1, 2) # mypy: OK
3
>>> add_numbers(1, 2.5) # mypy: OK
3.5
>>> add_numbers(1.5, 2.5) # mypy: OK
4.0
8.10.11. Further Reading¶
More information in Type Annotations
More information in CI/CD Type Checking