# 3.4. Type Annotation Function¶

## 3.4.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

## 3.4.2. Return¶

>>> def say_hello() -> str:
...     return 'My name... José Jiménez'


## 3.4.3. Parameters¶

>>> def add_numbers(a: int, b: int) -> int:
...     return a + b


## 3.4.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

>>>
... def add_numbers(a: int|float, b: int|float) -> int|float:
...     return a + b


## 3.4.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

>>>
... 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.11: PEP 645 -- Allow writing optional types as x?

>>>
... 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


## 3.4.6. Exception¶

>>> def stop() -> Exception:
...     raise RuntimeError

>>> from typing import Union
>>>
>>>
>>> def valid_email(email: str) -> Union[str,Exception]:
...     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


## 3.4.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


## 3.4.8. Callable¶

>>> from typing import Callable
>>>
>>>
>>> def feeder(get_next_item: Callable[[], str]) -> None:
...     pass

>>> from typing import Callable
>>>
>>>
>>> def async_query(on_success: Callable[[int], None],
...                 on_error: Callable[[int, Exception], None]) -> None:
...     pass


## 3.4.9. Iterator¶

>>> from typing import Iterator
>>>
>>>
>>> def fib(n: int) -> Iterator[int]:
...     a, b = 0, 1
...     while a < n:
...         yield a
...         a, b = b, a + b


## 3.4.10. Annotations¶

>>> def add(a: int, b: int) -> int:
...     return a + b
>>>
>>>
{'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}


Since Python 3.11: PEP 563 -- Postponed Evaluation of Annotations

>>> def add(a: int, b: int) -> int:
...     return a + b
>>>
>>>
{'a': 'int', 'b': 'int', 'return': 'int'}


## 3.4.11. 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
>>>
>>>
'MarkWatney'


## 3.4.12. Good Engineering Practices¶

>>> from typing import Union
>>>
>>>
...                 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