我试图找出一种很好的方式来为装饰器进行类型提示。这是一个简化/最小的示例。装饰器称为@stringify_input
,它将装饰函数的输入转换为字符串:
@stringify_input
def two_things(x: str) -> float:
return float(x + x)
assert two_things(1) == 11.0
# implementation with no hints:
def stringify_input(fn):
def inner(thing: int):
return fn(str(thing))
return inner
说我事先不知道装饰函数返回什么,但是我知道装饰器不会更改它。当前,未经装饰的函子返回一个float
,因此经过装饰的函子也应该返回。我已经知道如何使用TypeVar
来做到这一点:
# types check
T = TypeVar("T")
def stringify_input(fn: Callable[[str], T]) -> Callable[[int], T]:
def inner(thing: int) -> T:
return fn(str(thing))
return inner
这是我的问题:那些Callable[....]
提示很难读,如果有名字,它们会更容易理解(我的IRL装饰器更复杂)。所以我试试这个:
StringFn = Callable[[str], T]
IntFn = Callable[[int], T]
def stringify_input(fn: StringFn) -> IntFn:
def inner(thing: int) -> T:
return fn(str(thing)) # Returning Any from function declared to return 'T'
return inner
但是没有输入check:
Returning Any from function declared to return 'T'
为什么mypy不满意?我实际上只是提取了两个表达式!关于TypeVar
的工作方式?
答案 0 :(得分:4)
TypeVar
已拆分为不同范围内的同名几个类型变量。声明所有表达式使用相同范围的T
。
# TypeVar scope equals function scope
def stringify_input(fn: StringFn[T]) -> IntFn[T]:
def inner(thing: int) -> T:
...
通常在全局范围内声明TypeVars their substitution happens in a fixed scope。例如,可以在两个函数中使用相同的TypeVar来表示单独的实际类型替换。
T = TypeVar('T')
def foo(a: T): ...
def bar(b: T): ...
foo("bar!") # T -> str
bar(b"foo!") # T -> bytes
通过将类型移到函数之外,三个作用域对T
具有不同的含义。
StringFn = Callable[[str], T] # T1
IntFn = Callable[[int], T] # T2
def stringify_input(fn: StringFn) -> IntFn:
def inner(thing: int) -> T: # T3
...
return inner
由于StringFn
和IntFn
都具有免费的TypeVar,因此可以由另一个上下文的TypeVar对其进行参数化。通过功能范围的T
对其进行参数设置,以显示StringFn
,IntFn
和inner
的所有参数都由相同的{{1 }}:
T