Type Hints
Callable Types
Higher-order functions like sorting with a custom key, event callbacks, and plugin systems all need to accept functions as arguments. Callable types let you specify exactly what kind of function is expected - what arguments it takes and what it returns.
Callable Objects
Objects with __call__ are also callable:
callable_basics.py
# Callable basics
from typing import Callable
Op = Callable[[int, int], int]
# Functions match Callable types
def add(a: int, b: int) -> int:
return a + b
def mul(a: int, b: int) -> int:
return a * b
op: Op = add
print("add:", op(2, 3))
op = mul
print("mul:", op(2, 3))
# Lambda also matches
op = lambda a, b: a - b
print("sub:", op(7, 2))
key_functions.py
# Callable key functions
from typing import Callable
KeyFn = Callable[[str], int]
# A custom sort using a key function
def sort_strings(items: list[str], key: KeyFn) -> list[str]:
return sorted(items, key=key)
items =
print("by length:", sort_strings(items, key=len))
print("by last char:", sort_strings(items, key=lambda s: ord(s[-1])))
# Callable key functions
from typing import Callable
KeyFn = Callable[[str], int]
# A custom sort using a key function
def sort_strings(items: list[str], key: KeyFn) -> list[str]:
return sorted(items, key=key)
items =
print("by length:", sort_strings(items, key=len))
print("by last char:", sort_strings(items, key=lambda s: ord(s[-1])))
# Callable key functions
from typing import Callable
KeyFn = Callable[[str], int]
# A custom sort using a key function
def sort_strings(items: list[str], key: KeyFn) -> list[str]:
return sorted(items, key=key)
items =
print("by length:", sort_strings(items, key=len))
print("by last char:", sort_strings(items, key=lambda s: ord(s[-1])))
callbacks.py
# Callbacks
from typing import Callable
OnEvent = Callable[[str], None]
# Register/trigger
def trigger(event: str, handler: OnEvent) -> None:
handler(event)
def print_handler(event: str) -> None:
print("handled:", event)
trigger("login", print_handler)
trigger("logout", lambda e: print("lambda handled:", e))
higher_order.py
# Higher-order functions
from typing import Callable
UnaryInt = Callable[[int], int]
# Factory
def make_multiplier(factor: int) -> UnaryInt:
def mul(x: int) -> int:
return x * factor
return mul
by2 = make_multiplier(2)
by5 = make_multiplier(5)
print("by2(10) =", by2(10))
print("by5(10) =", by5(10))
callable_objects.py
# Callable objects
from typing import Callable
# A callable class
class Adder:
def __init__(self, n: int) -> None:
self.n = n
def __call__(self, x: int) -> int:
return x + self.n
add10 = Adder(10)
print("add10(5) =", add10(5))
# It can be treated like a Callable[[int], int]
Fn = Callable[[int], int]
f: Fn = add10
print("f(7) =", f(7))
When to Use Callable
- Callbacks / event handlers
- Customizing behavior (strategy pattern)
- Mapping / filtering utilities
- Dependency injection in small scripts
If you need many overloads, consider
Protocolinstead (beyond this page).
Callable - a type annotation `Callable[[ArgTypes...], ReturnType]` describing a function's signature
higher-order function - a function that takes another function as an argument or returns a function
Exercise: practical.py
Create a validator registry using Callable types