4.5. Callable

4.5.1. Rationale

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


callable(hello)
# True

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

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

4.5.2. Function Attributes

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


hello.myvar = 10
print(hello.myvar)
# 10
def hello():
    if not hello.disabled:
        print('My name... José Jiménez')
    else:
        raise PermissionError


hello.disabled = False
hello()
# My name... José Jiménez

hello.disabled = True
hello()
# Traceback (most recent call last):
# PermissionError
def add(a, b):
    return a + b


add.__code__.co_varnames
# ('a', 'b')

dir(add.__code__)
# [...,
#  'co_argcount',
#  'co_cellvars',
#  'co_code',
#  'co_consts',
#  'co_filename',
#  'co_firstlineno',
#  'co_flags',
#  'co_freevars',
#  'co_kwonlyargcount',
#  'co_lnotab',
#  'co_name',
#  'co_names',
#  'co_nlocals',
#  'co_posonlyargcount',
#  'co_stacksize',
#  'co_varnames',
#  'replace']
def add(a: int, b: int) -> int:
    return a + b


add.__annotations__
# {'a': int, 'b': int, 'return': int}

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

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

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

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

4.5.7. Case Studies

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

4.5.8. Assignments

Code 4.41. Solution
"""
* Assignment: Function First Class 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`

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`

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