我正在尝试编写一个简单的类型包装器来表示装饰器函数的接口:
from typing import Protocol, TypeVar, Generic
TIn = TypeVar('TIn', contravariant=True)
TOut = TypeVar('TOut', covariant=True)
class Decorator(Protocol, Generic[TIn, TOut]):
"""
Represents a decorated value, used to simplify type definitions
"""
def __call__(self, value: TIn) -> TOut:
...
这将用于键入装饰器函数,如下所示:
IntFunction = Callable[[int, int], int]
def register_operator(op: str) -> Decorator[IntFunction, IntFunction]:
def inner(value: IntFunction) -> IntFunction:
# register the function or whatever
return value
return inner
@register_operator("+")
def add(a: int, b: int) -> int:
return a + b
在上面的示例中,Mypy 能够验证
add
的类型签名,以确保它符合 register_operator
的规范。
这对于转换类型的装饰器很有用(例如将其从
IntFunction
转换为StrFunction
),但在几乎所有情况下,TIn
与TOut
相同,所以我想简化用法我的定义。
本质上,我想这样做,如果没有给出
TOut
,它将被假定为与TIn
相同,这将允许上面的装饰器函数简化为
def register_operator(op: str) -> Decorator[IntFunction]:
# Simplification here ^
def inner(value: IntFunction) -> IntFunction:
# register the function or whatever
return value
return inner
我在协议定义中使用的理想语法将是这样的:
class Decorator(Protocol, Generic[TIn, TOut = TIn]):
"""
Represents a decorated value, used to simplify type definitions
"""
def __call__(self, value: TIn) -> TOut:
...
请注意,这不起作用。
如何实现此功能,同时继续获得 Mypy 提供的保证?我很高兴根据需要使
Decorator
的定义变得复杂,但我想保持其简单的用法。
据我所知,python 不支持泛型中的默认值,未完全定义的泛型将变得未知。也许它应该支持它,有一个针对此功能的草稿(请参阅下面的更新)。
现在,我将创建两个公开不同接口的类。这几乎一样好,只是意味着您需要第二堂课:
from typing import Protocol, TypeVar, Generic, overload
TIn = TypeVar('TIn', contravariant=True)
TOut = TypeVar('TOut', covariant=True)
TSym = TypeVar('TSym')
class Decorator(Protocol, Generic[TIn, TOut]):
"""
Represents a decorated value, used to simplify type definitions
"""
def __call__(self, value: TIn) -> TOut:
...
class SymmetricDecorator(Decorator[TSym,TSym], Generic[TSym], Protocol):
pass
“对称”我只是指可调用接收并返回相同的类型 - 可能有更好的名称。希望这有用!
更新:我发现 TypeVar 的默认值存在一个活动的 pep:https://peps.python.org/pep-0696/