Microsoft の Python 向け型チェッカーの pyright の README を流し読みしていたら、初めて聞く PEP が登場したシリーズ第二弾です。
今回は PEP 612 (Parameter Specification Variables) について書きます。
概要
- 汎用のデコレータを書こうとすると、型を書くのが非常に難しい
- 指定された関数と同じ引数を持つ関数 を表現する方法がほしい
アプローチ
-
ParameterSpecification
という引数を表す型を追加すると解決する
引数を表す型として ParameterSpecification
を追加する。
ParameterSpecification
は Callable
と一緒に用いることで callable object のジェネリクスっぽく振る舞うことができる。 TypeVar
の引数版と考えるとわかりやすい。
例
以下の例では add_logging()
の引数は Callable[Ps, R]
型で、返り値も同じく Callable[Ps, R]
型である。つまり、引数として指定された関数と同じインターフェースの関数を返すデコレータである。
そのため、 @add_logging
でデコレートされた foo()
は (x: int, y: str) -> int
型のままである。
from typing import Callable, ParameterSpecification, TypeVar
Ps = ParameterSpecification("Ps")
R = TypeVar("R")
def add_logging(f: Callable[Ps, R]) -> Callable[Ps, R]:
def inner(*args: Ps.args, **kwargs: Ps.kwargs) -> R:
log_to_database()
return f(*args, **kwargs)
return inner
@add_logging
def foo(x: int, y: str) -> int:
return x + 7
ParameterSpecification
型が導入されない場合、こういったデコレータに型をつけることは難しい。
感想
- 幸いなことにこれまでデコレータに型をつけることがなかったので気づかなかったが、確かに
ParameterSpecification
がないと適切な型がつけられない -
ParameterSpecification
という名前はちょっと長いが、特に代案は思いつかなかったの仕方ない… - 引数をいじるタイプのデコレータはこの PEP では扱わないとされているので、全てが解決ではないのが残念
- まだ Draft なので採用は決まってません