假设我有一个带有值和任意数量函数的函数,让我们调用chain_call函数。
没有类型,简单的天真实现将是:
def chain_call(input_value, *args):
for function in args:
input_value = function(input_value)
return input_value
如您所想,input_value可能是真实的,但它始终与Callable
中第一个*args: List[Callable]
的第一个也是唯一必需的参数相同。
从此处开始,Callable
的第一个且唯一必需的参数与前一个返回类型的类型相同。
到目前为止,我已经设法定义了一个非常通用的类型,但它太松散了。
def chain_call(input_value: Any, *args: List[Callable[Any], Any]) -> Any: ...
我真正喜欢的是
T = TypeVar('T')
def chain_call(input_value: T, *args: List[Callable[T, ...], tr]) -> tr: ...
T
的{{1}} Callable n+1
为tr
,最终返回类型为Callable n
的tr。我不确定如何用类型系统表达这一点,并且会喜欢任何指导。
答案 0 :(得分:1)
不幸的是,目前这不是可以使用PEP 484类型提示键入的内容。
你可以做的最好的事情是使用重载来近似签名:基本上,我们硬编码签名应该达到一定数量然后再回到推断'任何':
from typing import TypeVar, overload, Any, Callable
T1 = TypeVar('T1')
T2 = TypeVar('T2')
T3 = TypeVar('T3')
T4 = TypeVar('T4')
@overload
def chain_call(input_value: T1,
*f_rest: Callable[[T1], T1]) -> T1: ...
@overload
def chain_call(input_value: T1,
f1: Callable[[T1], T2],
f2: Callable[[T2], T3],
f3: Callable[[T3], T4],
f4: Callable[[T4], Any],
*f_rest: Callable[[Any], Any]) -> Any: ...
@overload
def chain_call(input_value: T1,
f1: Callable[[T1], T2],
f2: Callable[[T2], T3],
f3: Callable[[T3], T4]) -> T4: ...
@overload
def chain_call(input_value: T1,
f1: Callable[[T1], T2],
f2: Callable[[T2], T3]) -> T3: ...
@overload
def chain_call(input_value: T1,
f1: Callable[[T1], T2]) -> T2: ...
def chain_call(input_value, *f_rest):
for function in f_rest:
input_value = function(input_value)
return input_value
在这里,我硬编码了最多3个输入函数应该发生的事情(并且在所有callables碰巧具有相同输入和输出类型的特殊情况下以重载开始)。
这种技术是类型化的当前类型如zip
函数的类型,它可以接受任意数量的迭代。
注意:您可能需要使用master中最新版本的mypy才能使此代码逐字处理。
答案 1 :(得分:1)
此完全类型化的函数存在于dry-python/returns
中。
我们称之为flow
:
from returns.pipeline import flow
assert flow('1', int, float, str) == '1.0'
问题是flow
是通过我们的库附带的自定义mypy
插件完全键入的。因此,它将捕获此错误情况(和many others):
from returns.pipeline import flow
def convert(arg: str) -> float:
...
flow('1', int, convert)
# error: Argument 1 to "convert" has incompatible type "int"; expected "str"
文档:https://returns.readthedocs.io/en/latest/pages/pipeline.html