パイソンのデコレータ型体操
24154 ワード
あなたの飾りを入力する必要がありますヒント
関数を呼び出す前にログを追加するためのシンプルなデコレータを持っているといいます.
import logging
def add_log(f):
def wrapper(*args, **kwargs):
logging.info("Called f!")
return f(*args, **kwargs)
return wrapper
@add_log
def two_sum(a, b):
return a + b
ある日、あなたはこのモジュールのタイプヒントを加えることに決めますtwo_sum
def two_sum(a: int, b: int) -> int:
return a + b
しかし、あなたのデコレータのタイプヒントを追加する必要がありますadd_log
この場合も、あなたは得るAny
ED関数.mypy 's reveal_type これを検証するために使用できます.import logging
def add_log(f):
def wrapper(*args, **kwargs): # type: ignore
logging.info("Called f!")
return f(*args, **kwargs)
return wrapper
def two_sum(a: int, b: int) -> int:
return a + b
reveal_type(two_sum) # Revealed type is 'def (a: builtins.int, b: builtins.int) -> builtins.int'
reveal_type(add_log(two_sum)) # Revealed type is 'Any'
単純な装飾子のための単純な型ヒント
この単純な飾り付けのためにタイプヒントを加えましょう.関数の引数と返り値を変更しない単純なデコレータについて
add_log
上)TypeVar その仕事はかなりうまくやるべきだ.from typing import TypeVar, Callable, cast
import logging
TCallable = TypeVar("TCallable", bound=Callable)
def add_log(f: TCallable) -> TCallable:
def wrapper(*args, **kwargs): # type: ignore
logging.info("Called f!")
return f(*args, **kwargs)
return cast(TCallable, wrapper)
@add_log
def two_sum(a: int, b: int) -> int:
return a + b
reveal_type(two_sum) # Revealed type is 'def (a: builtins.int, b: builtins.int) -> builtins.int'
reveal_type(add_log(two_sum)) # Revealed type is 'def (a: builtins.int, b: builtins.int) -> builtins.int'
ハードデコレータのハードタイプヒント
しかし、引数および/または戻り値を変更したい場合はどうですか?さて、引数を入力する簡単な方法はありませんが、少なくとも返り値を
from typing import TypeVar, Callable, Awaitable, cast
R = TypeVar('R')
def sync_to_async(f: Callable[..., R]) -> Callable[..., Awaitable[R]]:
async def wrapper(*args, **kwargs): # type: ignore
return f(*args, **kwargs)
return cast(Callable[..., Awaitable[R]], wrapper)
@sync_to_async
def two_sum(a: int, b: int) -> int:
return a + b
reveal_type(two_sum) # Revealed type is 'def (*Any, **Any) -> typing.Awaitable[builtins.int*]'
reveal_type(two_sum(2, "3")) # Revealed type is 'typing.Awaitable[builtins.int*]
引数と戻り値を入力するための暗い方法
あなたはいくつかのタイプの体操を行うことができます..数を生成することによって
TypeVar
sと使用overload
.from typing import TypeVar, Callable, Awaitable, overload
A = TypeVar('A')
B = TypeVar('B')
C = TypeVar('C')
D = TypeVar('D')
E = TypeVar('E')
RV = TypeVar('RV')
@overload
def sync_to_async(f: Callable[[A], RV]) -> Callable[[A], Awaitable[RV]]:
...
@overload
def sync_to_async(f: Callable[[A, B], RV]) -> Callable[[A, B], Awaitable[RV]]:
...
@overload
def sync_to_async(f: Callable[[A, B, C], RV]) -> Callable[[A, B, C], Awaitable[RV]]:
...
@overload
def sync_to_async(f: Callable[[A, B, C, D], RV]) -> Callable[[A, B, C, D], Awaitable[RV]]:
...
@overload
def sync_to_async(f: Callable[[A, B, C, D, E], RV]) -> Callable[[A, B, C, D, E], Awaitable[RV]]:
...
def sync_to_async(f):
async def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
@sync_to_async
def two_sum(a: int, b: int) -> int:
return a + b
@sync_to_async
def do_log(content: str) -> None:
print(content)
reveal_type(two_sum) # Revealed type is 'def (builtins.int*, builtins.int*) -> typing.Awaitable[builtins.int*]'
reveal_type(do_log) # Revealed type is 'def (builtins.str*) -> typing.Awaitable[None]'
このようにしたい場合は、上記のコードを生成するために使用したコードスニペットを示します.gymnastics = 5
for i in range(gymnastics):
char = chr(i + 65)
print(f"{char} = TypeVar('{char}')")
print("RV = TypeVar('RV')")
for i in range(gymnastics):
chars = [chr(n + 65) for n in range(i + 1)]
args = ", ".join(chars)
print(f"""@overload
def sync_to_async(f: Callable[[{args}], RV]) -> Callable[[{args}], Awaitable[RV]]:
...""")
未来:PEP - 612
PEP-612 定義ParamSpec and Concatenate . 彼らはタイプのヒントを飾ることができる非常に簡単です.
from typing import Concatenate, ParamSpec
P = ParamSpec('P')
R = TypeVar('R')
def with_context(f: Callable[Concatenate[Context, P], R]) -> Callable[P, R]:
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
return f(context, *args, **kwargs)
return inner
@with_context
def request(context: Context) -> int:
return 42
悲しいことはPEP-612 is not widely supported , 今のところ、MyPyは完全にそれをサポートしていません.フィン
タイプがあなたといるかもしれません.
Reference
この問題について(パイソンのデコレータ型体操), 我々は、より多くの情報をここで見つけました https://dev.to/whtsky/decorator-type-gymnastics-in-python-29kgテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol