英文:
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 数组的对象。
如果你想要标注函数 f
和 g
接受列表和 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论