18.8. Typing Callable
Before Python 3.9 you need
from typing import List, Set, Tuple, DictSince Python 3.9: PEP 585 -- Type Hinting Generics In Standard Collections
18.8.1. Parameters
Required Parameters
Optional Parameters
Mixed Parameters
>>> def run(a: int, b: int):
... ...
>>> def run(a: int = 0, b: int = 1):
... ...
>>> def run(a: int, b: int = 1):
... ...
18.8.2. Union and Optional
>>> def run(a: int | float, b: int | float):
... ...
>>> def run(a: int | None, b: int | None):
... ...
18.8.3. Return
Return Type
Return Union
Return Optional
>>> def run() -> int:
... ...
>>> def run(a: int | float, b: int | float) -> int | float:
... ...
>>> def run(a: int | None, b: int | None) -> int | None:
... ...
18.8.4. Return None
In Python functions always return something. If you don't specify a return
value, Python will return None.
>>> def run() -> None:
... return None
>>> def run() -> None:
... pass
>>> def run() -> None:
... print('hello')
18.8.5. NoReturn
If you want to indicate that a function
never returns, you can use NoReturn.
>>> from typing import NoReturn
>>> def run() -> NoReturn:
... pass
>>> def run() -> NoReturn:
... print('hello')
18.8.6. Exception
>>> from typing import NoReturn
>>>
>>> def run() -> NoReturn:
... raise ValueError
>>> def run() -> Exception:
... raise ValueError
>>> def run() -> ValueError:
... raise ValueError
>>> def run(value: int) -> int | ValueError:
... if value <= 0:
... raise ValueError
... else:
... return value
18.8.7. Literal
Literal de-duplicates parameters
Equality comparisons of Literal objects are not order dependent
https://docs.python.org/3/library/typing.html#typing.Literal
SetUp:
>>> from typing import Literal
Definition:
>>> def open(filename: str, mode: Literal['r','w','a']) -> None:
... pass
Usage:
>>> open('myfile.txt', mode='w') # ok
>>> open('myfile.txt', mode='r') # ok
>>> open('myfile.txt', mode='a') # ok
>>> open('myfile.txt', mode='x') # error
18.8.8. Callable
Function is Callable
CallableCallable[[int, int], float]is a function of(int, int) -> floatThere is no syntax to indicate optional or keyword arguments
https://docs.python.org/3/library/typing.html#typing.Callable
SetUp:
>>> from collections.abc import Callable
Define:
>>> def run(a: int, b: int) -> float:
... ...
>>>
>>> a: Callable = run
>>> b: Callable[..., float] = run
>>> c: Callable[[int,int], ...] = run
>>> d: Callable[[int,int], float] = run
Parameter:
>>> def run(func: Callable[[int, int], float]):
... ...
18.8.9. Convention
>>> def add(a: int | float,
... b: int | float,
... ) -> int | float:
... return a + b
18.8.10. Use Case - 1
>>> def valid_email(email: str) -> str | Exception:
... if '@' in email:
... return email
... else:
... raise ValueError('Invalid Email')
>>>
>>>
>>> valid_email('alice@example.com')
'alice@example.com'
>>>
>>> valid_email('alice_at_example.com')
Traceback (most recent call last):
ValueError: Invalid Email
18.8.11. Use Case - 2
>>> 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
18.8.12. Use Case - 3
>>> from collections.abc import Callable
>>> from typing import Any
>>> from urllib.request import urlopen
>>>
>>>
>>> def fetch(url: str,
... on_success: Callable[[str], Any] = lambda result: ...,
... on_error: Callable[[Exception], Any] = lambda error: ...,
... ) -> None:
... try:
... result: str = urlopen(url).read().decode('utf-8')
... except Exception as err:
... on_error(err)
... else:
... on_success(result)
>>> def handle_result(result: str) -> None:
... print('Success', result)
>>>
>>> def handle_error(error: Exception) -> None:
... print('Error', error)
>>>
>>>
>>> fetch(
... url='https://python3.info',
... on_success=handle_result,
... on_error=handle_error,
... )
>>> fetch(
... url='https://python3.info',
... on_success=lambda result: print(result),
... on_error=lambda error: print(error),
... )