Mypy在使用ArrayLike时出现错误。

huangapple go评论84阅读模式
英文:

Mypy errors when using ArrayLike

问题

I don't understand how I should be using ArrayLike in my code. If check mypy, I keep getting errors when I try to use the variables for anything without calling cast. I am trying to define function signatures that work with ndarray as well as regular lists.

For example, the code below:

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]))

give me these errors for f() and 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]

英文:

I don't understand how I should be using ArrayLike in my code. If check mypy, I keep getting errors when I try to use the variables for anything without calling cast. I am trying to define function signatures that work with ndarray as well as regular lists.

For example, the code below

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]))

give me theses errors for f() and 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]

答案1

得分: 3

numpy.typing.ArrayLike 用于标注可转换为 ndarray 的对象。它定义的类型是以下的联合类型:

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

_SupportsArray 是一个带有 __array__ 方法的协议,不要求实现 __len__(用于 len 函数)或 __getitem__(用于索引)。

_NestedSequence 是一个更为严格的协议,实际上要求实现 __len____getitem__

但问题在于,参数注释是一个联合类型

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[Any] | npt.NDArray[Any] 的联合类型。

如果你想要更广泛的标注,以适应具有 __len____getitem__ 的任何类型,你需要定义自己的协议

英文:

The purpose of numpy.typing.ArrayLike is to be able to annotate

> objects that can be coerced into an ndarray.

With that purpose in mind, they defined the type to be the following union:

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

_SupportsArray is simply a protocol with an __array__ method. It neither requires __len__ (for use with the len function) nor __getitem__ (for indexing) to be implemented.

_NestedSequence is a more restrictive protocol that does actually require __len__ and __getitem__.

But the problem with this code is that the parameter annotation is that union:

import numpy.typing as npt

...

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

So a could be a sequence-like object that supports __len__, but it could also be just an object that supports __array__ and nothing else. It could even be just an int for example (see the union again). Therefore the call len(a) is unsafe.

Similarly, here the item access is not type safe because a might not implement __getitem__:

...

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

So the reason it is not working for you is that it is not meant to be used as an annotation for things that are numpy arrays or other sequences; it is meant to be used for things that can be turned into numpy arrays.


If you want to annotate your functions f and g to take both lists and numpy arrays, you could just use a union of list and NDArray like list[Any] | npt.NDArray[Any].

If you want to have a wider annotation to accommodate any type that has __len__ and __getitem__, you need to define your own 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]))

To be more precise __getitem__ should probably also take slice objects, but the overloads may be overkill for you.

答案2

得分: 0

[mypy]
plugins = numpy.typing.mypy_plugin
英文:

Try to comply with the numpy.typing followed by suggested settings under mypy config

[mypy]
plugins = numpy.typing.mypy_plugin

huangapple
  • 本文由 发表于 2023年5月17日 17:45:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76270706.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定