3.11. FuncProg Callable

3.11.1. Rationale

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

3.11.2. Calling Call Method

  • __call__() method makes object callable

def hello():
    print('My name... José Jiménez')


type(hello)
# <class 'function'>

callable(hello)
# True

hello()
# My name... José Jiménez

hello.__call__()
# My name... José Jiménez

3.11.3. Overloading Call Method

astro = str('Mark Watney')

astro()
# Traceback (most recent call last):
# TypeError: 'str' object is not callable

callable(astro)
# False

type(astro)
# <class 'str'>
class str(str):
    def __call__(self):
        print('hello')


astro = str('Mark Watney')

astro()
# hello

callable(astro)
# True

type(astro)
# <class '__main__.str'>

3.11.4. Callbacks

Callback Design Pattern:

from http import HTTPStatus
import requests


def http_request(url, on_success=lambda: None, on_error=lambda: None):
    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.11.5. Type Annotation

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: None,
                 on_error: Callable = lambda: None) -> None:
    ...
from typing import Callable, Iterator, Iterable


def zip(a: Iterable, b: Iterable) -> Iterator:
    ...

def enumerate(data: Iterable) -> Iterator[int, Any]:
    ...

def map(func: Callable, data: Iterable) -> Iterator:
    ...

def filter(func: Callable, data: Iterable) -> Iterator:
    ...

3.11.6. Use Case

import datetime


now = datetime.datetime.now

print(now)
# <built-in method now of type object at 0x107695638>

print(now())
# 1969-07-21 02:56:25

now()
# datetime.datetime(1969, 7, 21, 2, 56, 25)

now.__call__()
# datetime.datetime(1969, 7, 21, 2, 56, 25)
import datetime
from time import sleep


now = datetime.datetime.now

print(now())          # 1969-07-21 02:56:15
sleep(10)
print(now())          # 1969-07-21 02:56:25

3.11.7. Assignments

Code 3.4. 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: None))
    >>> check(lambda: None)()
    hello from wrapper
"""