Concatenate は ParamSpec と組み合わせて、関数の引数に新しいパラメータを追加する型を表現します。
基本的な使い方
from typing import Callable, Concatenate, ParamSpec, TypeVar P = ParamSpec("P") T = TypeVar("T") def with_context( func: Callable[Concatenate[str, P], T] ) -> Callable[P, T]: def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: context = "default_context" return func(context, *args, **kwargs) return wrapper
Concatenate[str, P] は「最初に str 型の引数があり、その後に P の引数が続く」という意味です。
実用例:認証付きデコレータ
from typing import Callable, Concatenate, ParamSpec, TypeVar P = ParamSpec("P") T = TypeVar("T") class User: def __init__(self, name: str): self.name = name def authenticated( func: Callable[Concatenate[User, P], T] ) -> Callable[P, T]: def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: user = User("current_user") # 実際は認証処理 return func(user, *args, **kwargs) return wrapper @authenticated def get_profile(user: User, detailed: bool = False) -> dict: return {"name": user.name, "detailed": detailed} # 呼び出し時は user を渡さない result = get_profile(detailed=True)
メソッドに self を追加
from typing import Callable, Concatenate, ParamSpec, TypeVar P = ParamSpec("P") T = TypeVar("T") S = TypeVar("S") def bind_self( func: Callable[Concatenate[S, P], T] ) -> Callable[P, T]: ...
Concatenate の位置
Concatenate の追加パラメータは常に先頭に来ます。
# 正しい Concatenate[int, str, P] # int, str, その後に P の引数 # 不正(Concatenate の後に他のパラメータは置けない) # Concatenate[P, int] # エラー
Concatenate を使うと、デコレータが引数を追加・削除する場合でも、正確な型情報を維持できます。型チェッカーが呼び出しの正当性を検証できるため、バグの早期発見に役立ちます。