python - 使用 ArrayLike 时出现 Mypy 错误

标签 python numpy mypy python-typing

我不明白应该如何在代码中使用 ArrayLike。如果检查 mypy,当我尝试在不调用强制转换的情况下使用变量进行任何操作时,我会不断收到错误。我正在尝试定义与 ndarray 以及常规列表一起使用的函数签名。

例如下面的代码

import numpy.typing as npt
import numpy as np

from typing import Any

def f(a: npt.ArrayLike) -> int:
    return len(a)

def g(a: npt.ArrayLike) -> Any:
    return a[0]

print(f(np.array([0, 1])), g(np.array([0, 1])))
print(f([0, 1]), g([0, 1]))

给我 f() 和 g() 的这些错误:

Argument 1 to "len" has incompatible type "Union[_SupportsArray[dtype[Any]], _NestedSequence[_SupportsArray[dtype[Any]]], bool, int, float, complex, str, bytes, _NestedSequence[Union[bool, int, float, complex, str, bytes]]]"; expected "Sized"  [arg-type]

Value of type "Union[_SupportsArray[dtype[Any]], _NestedSequence[_SupportsArray[dtype[Any]]], bool, int, float, complex, str, bytes, _NestedSequence[Union[bool, int, float, complex, str, bytes]]]" is not indexable  [index]

最佳答案

numpy.typing.ArrayLike的目的就是能够注释

objects that can be coerced into an ndarray.

考虑到这一目标,他们 defined the type成为以下联合体:

Union[
    _SupportsArray[dtype[Any]],
    _NestedSequence[_SupportsArray[dtype[Any]]],
    bool,
    int,
    float,
    complex,
    str,
    bytes,
    _NestedSequence[Union[bool, int, float, complex, str, bytes]]
]

_SupportsArray只是一个带有 __array__ 方法的协议(protocol)。它既不需要实现 __len__ (与 len 函数一起使用),也不需要实现 __getitem__ (用于索引)。

_NestedSequence是一个限制性更强的协议(protocol),确实实际上需要 __len____getitem__

但是这段代码的问题是,参数注解是union:

import numpy.typing as npt

...

def f(a: npt.ArrayLike) -> int:
    return len(a)

所以a可能是一个支持__len__的类似序列的对象,但它也可能只是一个仅支持 __array__ 的对象。例如,它甚至可能只是一个 int (再次参见联合)。因此,调用 len(a) 是不安全的。

同样,这里的项目访问不是类型安全的,因为 a 可能不会实现 __getitem__:

...

def g(a: npt.ArrayLike) -> Any:
    return a[0]

所以它对你不起作用的原因是它不意味着用作 numpy 数组或其他序列的注释;它旨在用于可以转换为 numpy 数组的东西。


如果您想注释函数 fg 以同时获取列表和 numpy 数组,您可以只使用 list 的并集和 NDArray就像 list[Any] | npt.NDArray[任意].

如果您想要更宽的注释来容纳任何具有 __len____getitem__ 的类型,您需要定义自己的 protocol :

from typing import Any, Protocol, TypeVar

import numpy as np

T = TypeVar("T", covariant=True)


class SequenceLike(Protocol[T]):
    def __len__(self) -> int: ...
    def __getitem__(self, item: int) -> T: ...


def f(a: SequenceLike[Any]) -> int:
    return len(a)


def g(a: SequenceLike[T]) -> T:
    return a[0]


print(f(np.array([0, 1])), g(np.array([0, 1])))
print(f([0, 1]), g([0, 1]))

更准确地说,__getitem__ 可能还应该采用 slice 对象,但重载对您来说可能有点过分了。

关于python - 使用 ArrayLike 时出现 Mypy 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76270706/

相关文章:

python - Python中是否有 "Addable"协议(protocol)或抽象基类?如果不是,又该如何定义呢?

django - mypy 和 django 模型 : how to detect errors on nonexistent attributes

python - 带有描述符的类型提示

python - pandas 在非字母表的任何字符上分割字符串列

python - 在 seaborn.despine() 上动态设置轴偏移

python - requirements.txt:以python版本为条件

python - numpy.save 是跨平台的吗?

python - 将元组列表转换为切片列表以与 np.r_ 一起使用

python - 为什么从 WAV 文件导出的 CSV 文件比原始 WAV 文件大得多?

python - 拆分 Django 项目