3.12. FuncProg Callable

3.12.1. Rationale

>>> def hello():
...     return 'Hello World'
>>>
>>>
>>> callable(hello)
True
>>>
>>> type(hello)
<class 'function'>
>>>
>>> hello  
<function hello at 0x...>
>>>
>>> hello()
'Hello World'

3.12.2. Calling Call Method

  • __call__() method makes object callable

>>> def hello():
...     return 'Hello World'
>>>
>>>
>>> hello()
'Hello World'
>>>
>>> hello.__call__()
'Hello World'

3.12.3. Overloading Call Method

>>> data = str('Mark Watney')
>>>
>>> data()
Traceback (most recent call last):
TypeError: 'str' object is not callable
>>>
>>> callable(data)
False
>>> class str(str):
...     def __call__(self):
...         print('Calling str')
>>>
>>>
>>> data = str('Mark Watney')
>>>
>>> data()
Calling str
>>>
>>> callable(data)
True

3.12.4. Callbacks

Callback Design Pattern:

>>> def request(url, on_success, on_error):
...     try:
...         result = f'Downloading from {url}'
...     except Exception as error:
...         on_error(error)
...     else:
...         on_success(result)
>>>
>>>
>>> request(
...     url='https://python.astrotech.io',
...     on_success=lambda result: print(result),
...     on_error=lambda error: print(error))
Downloading from https://python.astrotech.io
>>> from http import HTTPStatus
>>> import requests
>>>
>>>
>>> def http_request(url, on_success=lambda: ..., on_error=lambda: ...):
...     result = requests.get(url)
...     if result.status_code == HTTPStatus.OK:
...         return on_success(result)
...     else:
...         return on_error(result)
>>>
>>>
>>> http_request(
...     url='http://python.astrotech.io/',
...     on_success=lambda result: print(result),
...     on_error=lambda error: print(error))
<Response [200]>

3.12.5. Type Annotation

>>> from typing import Callable
>>>
>>>
>>> def add(a: int, b: int) -> int:
...     return a + b
>>>
>>>
>>> total: Callable = add
>>> total: Callable[[int, int], int] = add
>>> from typing import Callable
>>>
>>>
>>> def lower() -> str:
...     return 'hello'
>>>
>>>
>>> def higher() -> Callable:
...     return lower
>>> from typing import Callable
>>>
>>>
>>> def http_request(url: str,
...                  on_success: Callable = lambda: ...,
...                  on_error: Callable = lambda: ...) -> None:
...     ...
>>> from typing import Callable
>>>
>>>
>>> def request(url: str,
...             on_success: Callable[[int,int], int],
...             on_error: Callable) -> callable:
...     ...
>>>
>>> def handle_success(x: int, y: int) -> int:
...     ...
>>>
>>> request('https://...',
...         on_success=handle_success,
...         on_error=lambda: ...)
>>> from typing import Callable, Iterator, Iterable
>>>
>>>
>>> def map(func: Callable, data: Iterable) -> Iterator:
...     ...
>>>
>>> def filter(func: Callable, data: Iterable) -> Iterator:
...     ...

3.12.6. Assignments

Code 3.31. Solution
"""
* Assignment: FuncProg Callable Define
* Complexity: easy
* Lines of code: 4 lines
* Time: 5 min

English:
    1. Define function `wrapper`
    2. Function `wrapper` takes arbitrary number of positional
       and keyword arguments
    3. Function `wrapper` prints `hello from wrapper`
    4. Define function `check` with `func: Callable` as a parameter
    5. Function `check` must return `wrapper: Callable`
    6. Run doctests - all must succeed

Polish:
    1. Zdefiniuj funkcję `wrapper`
    2. Funkcja `wrapper` przyjmuje dowolną ilość argumentów pozycyjnych
       i nazwanych
    3. Funkcja `wrapper` wypisuje `hello from wrapper`
    4. Zdefiniuj funkcję `check` z `func: Callable` jako parametr
    5. Funkcja `check` ma zwracać `wrapper: Callable`
    6. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isfunction

    >>> assert isfunction(check)
    >>> assert isfunction(wrapper)
    >>> assert isfunction(check(lambda: ...))
    >>> check(lambda: ...)()
    hello from wrapper
"""


# Callable: takes arbitrary number of positional and keyword arguments
#           prints `hello from wrapper`
def wrapper():
    ...


# Callable: takes `func` as an argument, returns wrapper function
def check():
    ...