数学講師2852771 views
高校生物549842 views
ヒストリア284143 views
英語607877 views
高校化学2913383 views
りんご192546 views
MathPython491378 views
高校倫理1433119 views
小学算数1194618 views
小学社会308636 views
Help
Tools

English

Callable — 関数の型ヒント

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] — 引数の型と個数が固定の場合。型チェックが厳密に効く。

Ellipsis を使用

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:
    ...
Callable は関数の型を表す

引数の型リストと戻り値の型で関数のシグネチャを記述する。コールバックや高階関数に不可欠。

ParamSpec で引数情報を保持

デコレータなど、関数を包む関数で元のシグネチャを維持したいときに使う。Python 3.10 以降で利用可能。

関数を値として受け渡す場面は、イベント処理、プラグインシステム、テストのモック、非同期タスクの登録など多岐にわたる。Callable で型を明示しておけば、渡す関数のシグネチャが合っているかを実行前に検証でき、バグの早期発見につながる。