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 なので採用は決まってません