gpt4 book ai didi

python - mypy 中的计算类型

转载 作者:太空狗 更新时间:2023-10-30 01:31:29 28 4
gpt4 key购买 nike

旁白:这个问题的标题并不理想。我尝试做的事情可以通过计算类型实现,也可以通过其他方式实现。

我正在编写一些代码来验证并有时将动态类型的 JSON 数据转换为静态 Python 类型。以下是一些功能:

def from_str(x: Any) -> str:
assert isinstance(x, str)
return x


def from_int(x: Any) -> int:
assert isinstance(x, int)
return x

def from_list(f: Callable[[Any], T], x: Any) -> List[T]:
assert isinstance(x, list)
return [f(y) for y in x]

这些效果很好。我还希望能够将它们组合起来以转换联合类型。理想情况下是这样的:

union = from_union([from_str, from_int], json)

问题是如何输入 from_union功能。我的第一个方法是这样的:

def from_union(fs: Iterable[Callable[[Any], T]], x: Any) -> T:
for f in fs:
try:
return f(x)
except AssertionError:
pass
assert False

从技术上讲这是正确的。如果我们替换 Union[str,int]对于 T上面的表达式输入正确,因为 from_str ,凭借返回 str还返回 Union[str,int] (任何 str 类型的值都是 Union[str,int] 类型的值)。然而,mypy不想做这个替换:

test/fixtures/python/quicktype.py:59: error: Argument 1 to "from_union" has incompatible type "List[Callable[[Any], object]]"; expected "Iterable[Callable[[Any], <nothing>]]"

它似乎直接转到object而不是推断 Union[str,int] .

理想情况下,我想给 from_union 的类型有点像

def from_union(fs: Iterable[Union[[Callable[[Any], S], Callable[[Any], T], ...]], x: Any) -> Union[S, T, ...]):

这在 Python 的类型中不受支持。另一种选择是能够指定一个可以计算 fs 类型的函数。来自特定调用的实际返回类型,或相反。这样的事情可能吗?有没有其他选择可以做到这一点而不必求助于 cast

最佳答案

正如您推断的那样,不幸的是,这在 Python 的类型系统中是不可能表达的。最好的解决方法(这与 Typeshed 用于键入 mapfilterzip 等内置函数的方法相同)是滥用重载,例如所以:

from typing import Iterable, Callable, Any, Union, TypeVar, overload, List

T1 = TypeVar('T1')
T2 = TypeVar('T2')
T3 = TypeVar('T3')

# Note: the two underscores tell mypy that the argument is positional-only
# and that doing things like `from_union(blob, f1=from_str)` is not legal

@overload
def from_union(x: Any,
__f1: Callable[[Any], T1],
) -> T1: ...

@overload
def from_union(x: Any,
__f1: Callable[[Any], T1],
__f2: Callable[[Any], T2],
) -> Union[T1, T2]: ...

@overload
def from_union(x: Any,
__f1: Callable[[Any], T1],
__f2: Callable[[Any], T2],
__f3: Callable[[Any], T3],
) -> Union[T1, T2, T3]: ...

# The fallback: give up on the remaining callables
@overload
def from_union(x: Any,
__f1: Callable[[Any], T1],
__f2: Callable[[Any], T2],
__f3: Callable[[Any], T3],
*fs: Callable[[Any], Any]
) -> Union[T1, T2, T3, Any]: ...

def from_union(x: Any, *fs: Callable[[Any], Any]) -> Any:
for f in fs:
try:
return f(x)
except AssertionError:
pass
assert False

这个函数基本上做的是硬编码以支持最多三个可调用对象,如果您尝试再传入,就会放弃。自然地,为了支持接受更多的可调用对象,添加更多的重载。

这个新函数的 API 确实有细微的变化:它需要像这样调用:

my_union = from_union(json_blob, from_str, from_int)

如果您想要一个与原始 API 更相似的 API 并将函数放在首位,您要么需要将 x 转换为仅包含关键字的参数(例如 from_union(* fs: Callable[[Any], Any], *, x: Any) -> Any) 或将函数存储在元组中,如下所示:

@overload
def from_union(fs: Tuple[Callable[[Any], T1]], x: Any) -> T1: ...

@overload
def from_union(fs: Tuple[Callable[[Any], T1], Callable[[Any], T2]], x: Any) -> Union[T1, T2]: ...

# etc...

# The final fallback: have the tuple accept any number of callables
@overload
def from_union(fs: Tuple[Callable[[Any], Any], ...], x: Any) -> Any: ...

def from_union(fs: Tuple[Callable[[Any], Any], ...], x: Any) -> Any:
for f in fs:
try:
return f(x)
except AssertionError:
pass
assert False

在这两种情况下,如果用户传入太多参数,“后备”将在输出中引入一些动态。如果您不喜欢这个,只需删除最后的后备。

关于python - mypy 中的计算类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51066468/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com