Pyright 无法猜测多态 python 函数中的数据类型

问题描述 投票:0回答:1

问题应该很简单。我有一个接受字典的函数,其中值必须是 float、int 或 numpy.ndarray 类型。我可以使用 ArrayLike 类型。 在里面,我有两个函数专门处理标量值(float、int)或数组值。 这是一个代码示例:

from typing import Dict, Union, Mapping
import numpy as np
from numpy.typing import ArrayLike

def fun_for_float(data: dict[str, Union[float,int]]) -> None:
    pass

def fun_for_array(data: dict[str, np.ndarray]) -> None:
    pass

def main_function(data: dict[str, ArrayLike]) -> None:
    if all(isinstance(val, Union[float,int]) for val in data.values()):
        return fun_for_float(data)
    elif all(isinstance(val, np.ndarray) for val in data.values()):
        return fun_for_array(data)
    else:
        raise ValueError("Values in the dictionary must be float or np.ndarray.")

问题是,pyright 接缝不理解我的类型守卫。 我收到以下错误:

Argument of type "dict[str, ArrayLike]" cannot be assigned to parameter "data" of type "dict[str, float | int]" in function "fun_for_float"
  "dict[str, ArrayLike]" is incompatible with "dict[str, float | int]"
    Type parameter "_VT@dict" is invariant, but "ArrayLike" is not the same as "float | int"
    Consider switching from "dict" to "Mapping" which is covariant in the value type

现在,我明白了。字典使用不变值,好吧。 ArrayLike 肯定与 float 或 int 不同,但是 float 和 int 应该包含在其中,并且通过 if 语句,pyrigth 应该能够缩小数据类型。

无论如何,我遵循了pyright的建议并使用了Mapping。

def fun_for_float(data: Mapping[str, Union[float,int]]) -> None:
    pass

def fun_for_array(data: Mapping[str, np.ndarray]) -> None:
    pass

def main_function(data: Mapping[str, ArrayLike]) -> None:
    if all(isinstance(val, Union[float,int]) for val in data.values()):
        return fun_for_float(data)
    elif all(isinstance(val, np.ndarray) for val in data.values()):
        return fun_for_array(data)
    else:
        raise ValueError("Values in the dictionary must be float or np.ndarray.")

这就是我得到的。

Argument of type "Mapping[str, ArrayLike]" cannot be assigned to parameter "data" of type "Mapping[str, float | int]" in function "fun_for_float"
  "Mapping[str, ArrayLike]" is incompatible with "Mapping[str, float | int]"
    Type parameter "_VT_co@Mapping" is covariant, but "ArrayLike" is not a subtype of "float | int"

现在,如果没有类型保护,pyright 是 100% 正确的,它应该抱怨。 ArrayLike 绝对不是 Union[float | int],事实恰恰相反。

有两个原因可以解释该错误:

  1. (不太可能)pyright 无法理解
    all(isinstance()...)
    类型检查。
  2. (更有可能)我搞砸了并且遗漏了一些东西。

有什么建议吗?

python type-hinting pyright
1个回答
0
投票

问题发布后不久,我意外地解决了问题。 如果有人需要的话,我将答案留在这里。 假设这两种可能性在某种程度上都是正确的。 Pyright 无法自主解释

all(asinstance())
结构。而且我搞砸了,不知道你可以定义明确的 TypeGuards(我没有阅读文档的错)。

为了帮助pyright,可以构造显式类型保护。

这是一个更简单的例子

def is_float(data: List[int|float]) -> TypeGuard[List[float]]:
    return all(isinstance(val, float) for val in data)

def is_int(data: List[int|float]) -> TypeGuard[List[int]]:
    return all(isinstance(val,int) for val in data)

def handle_int_list(data: List[int]) -> None:
    pass

def handle_float_list(data: List[float]) -> None:
    pass
def process_data(data: List[Union[int, float]]) -> None:
    if is_int(data):
        handle_int_list(data)
    elif is_float(data):
        handle_float_list(data)
    else:
        print("Data contains mixed types")

© www.soinside.com 2019 - 2024. All rights reserved.