如何为装饰器的通用类型起别名
How to alias generic types for decorators
考虑 。
import unittest
from typing import *
T = TypeVar("T", bound=unittest.TestCase)
def decorate(func: Callable[[T], None]) -> Callable[[T], None]:
def decorated_function(self: T) -> None:
return func(self)
return decorated_function
现在我什至有 a generator that creates these decorators 并且想要 shorthand 这些装饰器。我对存储装饰器的变量 variables 做什么样的类型(省略生成器的简化示例)。
my_decorate: Callable[[Callable[[T], None]], Callable[[T], None]] = decorate
这可行,但很笨重。所以问题是:
如何为这种类型起别名以避免必须编写完整签名?
不起作用的东西:
TD = Callable[[Callable[[T], None]], Callable[[T], None]]
my_decorate: TD[T] = decorator_variable
报错
error: Type variable "mypytest.T" is unbound
note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class)
note: (Hint: Use "T" in function signature to bind "T" inside a function)
相比之下,我可以使用 TD[T]
作为函数的参数类型。
仅使用 my_decorate: TD = ...
会产生 --strict
错误
error: Missing type parameters for generic type "TD"
并且它不再检测 my_decorate
的错误应用。
这个呢?它比完整签名短:
import unittest
from typing import *
T = TypeVar("T", bound=unittest.TestCase)
def decorate(func: Callable[[T], None]) -> Callable[[T], None]:
def decorated_function(self: T) -> None:
return func(self)
return decorated_function
decorator_variable: Callable[[Callable[[T], None]], Callable[[T], None]] = decorate
U = Callable[[T], None]
my_decorate: Callable[[U[T]], U[T]] = decorator_variable
在许多情况下,当 Callable
太有限时,请改用 Protocol
:
class TD(Protocol):
"""Type of any callable `(T -> None) -> (T -> None)` for all `T`"""
def __call__(self, __original: Callable[[T], None]) -> Callable[[T], None]:
...
TD
不是 通用类型,因此不需要“填写”类型变量。可以直接作为注解使用:
my_decorate: TD = decorate
值得注意的是,TD.__call__
仍然是一个通用的可调用对象,尽管 TD
不是通用的。它的 T
由每次调用的上下文根据需要填充。
考虑
import unittest
from typing import *
T = TypeVar("T", bound=unittest.TestCase)
def decorate(func: Callable[[T], None]) -> Callable[[T], None]:
def decorated_function(self: T) -> None:
return func(self)
return decorated_function
现在我什至有 a generator that creates these decorators 并且想要 shorthand 这些装饰器。我对存储装饰器的变量 variables 做什么样的类型(省略生成器的简化示例)。
my_decorate: Callable[[Callable[[T], None]], Callable[[T], None]] = decorate
这可行,但很笨重。所以问题是:
如何为这种类型起别名以避免必须编写完整签名?
不起作用的东西:
TD = Callable[[Callable[[T], None]], Callable[[T], None]]
my_decorate: TD[T] = decorator_variable
报错
error: Type variable "mypytest.T" is unbound
note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class)
note: (Hint: Use "T" in function signature to bind "T" inside a function)
相比之下,我可以使用 TD[T]
作为函数的参数类型。
仅使用 my_decorate: TD = ...
会产生 --strict
错误
error: Missing type parameters for generic type "TD"
并且它不再检测 my_decorate
的错误应用。
这个呢?它比完整签名短:
import unittest
from typing import *
T = TypeVar("T", bound=unittest.TestCase)
def decorate(func: Callable[[T], None]) -> Callable[[T], None]:
def decorated_function(self: T) -> None:
return func(self)
return decorated_function
decorator_variable: Callable[[Callable[[T], None]], Callable[[T], None]] = decorate
U = Callable[[T], None]
my_decorate: Callable[[U[T]], U[T]] = decorator_variable
在许多情况下,当 Callable
太有限时,请改用 Protocol
:
class TD(Protocol):
"""Type of any callable `(T -> None) -> (T -> None)` for all `T`"""
def __call__(self, __original: Callable[[T], None]) -> Callable[[T], None]:
...
TD
不是 通用类型,因此不需要“填写”类型变量。可以直接作为注解使用:
my_decorate: TD = decorate
值得注意的是,TD.__call__
仍然是一个通用的可调用对象,尽管 TD
不是通用的。它的 T
由每次调用的上下文根据需要填充。