3.7. Callable

3.7.1. Rationale

def hello():
    print('Hello')


callable(hello)
# True

hello()
# Hello

3.7.2. Function Object

def hello():
    print('Hello')


type(hello)
# <class 'function'>

hello.__call__()
# Hello

3.7.3. First-Class Function

  • Function can be assigned to a variable

  • Function can be used as parameters

  • Function can be used as a return value

  • Function can be stored in data structures such as lists, mappings, etc

def lower():
    return 'hello'


def higher():
    return lower


result = higher()     # <function __main__.lower()>
result()              # 'hello'
def hello():

3.7.4. Callable

  • __call__() method makes object callable

def hello():
    print('Hello')


type(hello)
# <class 'function'>

callable(hello)
# True

hello()
# Hello

hello.__call__()
# Hello
def hello():
    return 'My name... José Jiménez'


type(hello())         # <class 'str'>
hello()               # My name... José Jiménez

type(hello)           # <class 'function'>
hello                 # <function hello at 0x0C55D420>
import datetime
import time


now = datetime.datetime.now

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

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

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

now.__call__()
# datetime.datetime(1969, 7, 21, 2, 56, 25)
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.7.5. Callbacks

Listing 3.151. Callback Design Pattern
from http import HTTPStatus
import requests


def http_request(url, on_success=lambda *args: None, on_error=lambda *args: 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.7.6. 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 *args: None,
                 on_error: Callable = lambda *args: 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.7.7. Case Studies

from datetime import datetime
from time import sleep


now = datetime.now

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

3.7.8. Assignments

3.7.8.1. Function First Class Define

  • Assignment name: Function First Class Define

  • Last update: 2020-10-13

  • Complexity level: easy

  • Lines of code to write: 4 lines

  • Estimated time of completion: 5 min

  • Solution: solution/function_firstclass_define.py

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

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

>>> from inspect import isfunction
>>> assert isfunction(check)
>>> assert isfunction(wrapper)
>>> assert isfunction(check(lambda: None))
>>> check(lambda: None)()
hello from wrapper