Python では関数もオブジェクトであり、変数に代入したり他の関数に渡したりできる。こうした「関数を値として扱う」場面で型ヒントを付けるには Callable を使う。引数の型と戻り値の型を明示することで、コールバックやデコレータのシグネチャを正確に表現できるようになる。
Callable の基本構文
Callable は collections.abc モジュールからインポートして使う。
from collections.abc import Callable def apply(func: Callable[[int, int], int], a: int, b: int) -> int: return func(a, b) def add(x: int, y: int) -> int: return x + y result = apply(add, 3, 5) # 8
Callable[[int, int], int] は「int を 2 つ受け取り、int を返す関数」を意味する。最初のリストが引数の型、リストの外が戻り値の型だ。
Python 3.8 以前では typing.Callable を使う必要があったが、3.9 以降は collections.abc.Callable が推奨されている。古いコードでは from typing import Callable と書かれていることも多いが、collections.abc.Callableのほうが現在の標準である。
typing.Callable は将来的に非推奨となる可能性がある。
引数なしの関数
引数を取らない関数は、空のリストで表現する。
from collections.abc import Callable def run_task(task: Callable[[], str]) -> str: return task() def greeting() -> str: return "Hello!" print(run_task(greeting)) # Hello!
Callable[[], str] が「引数なしで str を返す関数」を意味する。リストの中身が空であることに注意しよう。
戻り値が None の関数
副作用だけを持つ関数には、戻り値に None を指定する。
from collections.abc import Callable def on_click(handler: Callable[[str], None]) -> None: handler("button_1") def log_click(element_id: str) -> None: print(f"Clicked: {element_id}") on_click(log_click)
イベントハンドラやログ関数のように「何かを実行するが値は返さない」パターンでよく使う形だ。
実用例:ソート関数のキー
標準ライブラリの sorted が受け取る key 引数も Callable で型を付けられる。
from collections.abc import Callable def sort_by(items: list[str], key: Callable[[str], int]) -> list[str]: return sorted(items, key=key) words = ["banana", "apple", "fig"] result = sort_by(words, key=len) # ["fig", "apple", "banana"]
key: Callable[[str], int] は「文字列を受け取って整数を返す関数」だ。len はまさにこのシグネチャに合致するので、そのまま渡せる。
高階関数とデコレータ
関数を返す関数にも Callable は使える。デコレータを型付けする典型的なパターンを見てみよう。
from collections.abc import Callable from functools import wraps import time def timer(func: Callable[..., float]) -> Callable[..., float]: @wraps(func) def wrapper(*args: object, **kwargs: object) -> float: start = time.time() result = func(*args, **kwargs) print(f"{func.__name__}: {time.time() - start:.3f}s") return result return wrapper @timer def compute(n: int) -> float: return sum(range(n)) * 1.0
Callable[..., float] の ...(Ellipsis)は「任意の引数を受け取る」ことを示す。引数の型を厳密に指定できない場合に使う記法で、デコレータのように汎用的な関数を包むときに便利だ。
Callable[[int, str], bool] — 引数の型と個数が固定の場合。型チェックが厳密に効く。
Callable[..., bool] — 引数の型を問わない場合。柔軟だが型チェックは緩くなる。
ParamSpec でデコレータを正確に型付けする
Callable[..., T] は便利だが、元の関数のシグネチャが失われてしまう。Python 3.10 で導入された ParamSpec を使えば、引数の型情報を保持したままデコレータを定義できる。
from collections.abc import Callable from typing import ParamSpec, TypeVar from functools import wraps P = ParamSpec("P") T = TypeVar("T") def retry(func: Callable[P, T]) -> Callable[P, T]: @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: for _ in range(3): try: return func(*args, **kwargs) except Exception: continue raise RuntimeError("retries exhausted") return wrapper @retry def fetch(url: str, timeout: int = 30) -> str: ...
ParamSpec("P") が元の関数の引数情報をまるごと保持する。これにより、デコレータを通しても fetch の引数の型ヒントが正しく維持される。mypy や IDE の補完もきちんと動く。
TypeAlias と組み合わせる
Callable の型が長くなる場合は、前の記事で紹介した TypeAlias と組み合わせると読みやすくなる。
from collections.abc import Callable from typing import TypeAlias EventHandler: TypeAlias = Callable[[str, dict[str, object]], None] Middleware: TypeAlias = Callable[[dict[str, object]], dict[str, object]] def register_handler(event: str, handler: EventHandler) -> None: ... def add_middleware(mw: Middleware) -> None: ...
引数の型リストと戻り値の型で関数のシグネチャを記述する。コールバックや高階関数に不可欠。
デコレータなど、関数を包む関数で元のシグネチャを維持したいときに使う。Python 3.10 以降で利用可能。
関数を値として受け渡す場面は、イベント処理、プラグインシステム、テストのモック、非同期タスクの登録など多岐にわたる。Callable で型を明示しておけば、渡す関数のシグネチャが合っているかを実行前に検証でき、バグの早期発見につながる。