为numpy.ndarray提供类型提示/注释(PEP 484)

202

有人是否实现了针对特定的numpy.ndarray类的类型提示?

目前,我正在使用typing.Any,但更具体的内容会更好。

例如,如果NumPy团队为其array_like对象类别添加类型别名,那就更好了。更好的做法是,在dtype级别实现支持,以便支持其他对象以及ufunc


1
https://pypi.python.org/pypi/plac 可以利用Py3注释来填充一个argparse解析器。对于Py2,它使用装饰器来创建一个类似的annotation数据库。 - hpaulj
1
typing 是 Py 3.5 中的新功能。许多 numpy 用户仍在使用 Py2。我在我的系统上有 3.5,但我没有为其安装 numpynumpy 开发人员不会为 Python 的前沿添加功能(除了 @ 运算符)。 - hpaulj
1
numpy 维护在 github 存储库上。查看 issuespull requests;注册并提交您自己的问题。可能有另一个讨论开发问题的论坛,但我大多数情况下会查看 github 的问题。 - hpaulj
7
如果有人正在研究这个问题,看起来这里有一个相关的解决方案:https://dev59.com/6yqE14gBEkM9YrnyewF1 - Itamar Mushkin
2
现在已经过去4.5年了,@Jasha,这个工单是由我作为原帖发起的。 - Inon
显示剩余3条评论
5个回答

80
Numpy 1.21版新增了一个 numpy.typing 模块,其中包含一个NDArray通用类型。
来自Numpy 1.21文档
numpy.typing.NDArray = numpy.ndarray[typing.Any, numpy.dtype[+ScalarType]]

A generic version of np.ndarray[Any, np.dtype[+ScalarType]].

Can be used during runtime for typing arrays with a given dtype and unspecified shape.

Examples:

>>> import numpy as np
>>> import numpy.typing as npt

>>> print(npt.NDArray)
numpy.ndarray[typing.Any, numpy.dtype[+ScalarType]]

>>> print(npt.NDArray[np.float64])
numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]]

>>> NDArrayInt = npt.NDArray[np.int_]
>>> a: NDArrayInt = np.arange(10)

>>> def func(a: npt.ArrayLike) -> npt.NDArray[Any]:
...     return np.array(a)
截至2022-09-05,支持形状仍在numpy/numpy#16544中进行。

3
我只是在想,如果在类型提示中使用ndarray而不是NDArray会有什么根本性的区别吗? - Jiadong
2
看一下NDarray的定义,似乎(1)在运行时有区别(因为NDArray是一个通用别名,而ndarray是一个类),以及(2)在类型检查时(例如使用mypy、pyright等),NDarray[Foo]np.ndarray[Any,np.dtype[Foo]]之间应该没有区别。 - Jasha
有没有一种方法来定义维数(向量、矩阵、三维等)的数量? - Royi
不,Numpy目前不支持这个功能。在Python生态系统中有长期的努力来支持这样的形状提示,例如PEP 646最近在Python3.11中引入。我认为,Numpy可能会通过PEP 646逐渐支持形状提示/维数提示,但实现和推出可能需要很长时间。与此同时,有第三方库,如nptyping,提供了对Numpy的类型提示,并支持形状提示。 - Jasha
1
np.ndarray[...]类型提示的第一个参数是什么,为什么所有示例中它都是Any - Joooeey
我认为第一个参数是用于未来使用的“形状注释”,您可以向类型检查器提示有关您期望数组具有的维度的信息。示例使用 Any,因为 numpy 尚未实现对此类注释的支持。 - Jasha

71

更新

检查最新版NumPy是否有一个新的typing模块。

https://numpy.org/doc/stable/reference/typing.html#module-numpy.typing

过时的答案

看起来typing模块是在这个版本开发的:

https://github.com/python/typing

主要的numpy代码库在这里:

https://github.com/numpy/numpy

Python的错误和提交可以在这里跟踪:

http://bugs.python.org/

添加功能的通常方式是fork主要代码库,开发功能直到它足够稳定,然后提交拉取请求。显然,在这个过程的不同阶段,您需要其他开发人员的反馈。如果您无法自己进行开发,则必须说服其他人这是一个有价值的项目。

cython具有一种注释形式,它用于生成高效的C代码。


您在numpy文档中引用了array-like段落。请注意其typing信息:

查找对象是否可以使用array()转换为NumPy数组的简单方法是在交互式环境中尝试并查看是否有效!(Python方式)。

换句话说,NumPy开发人员拒绝被固定下来。他们不会或不能用语言描述哪些对象可以或不可以转换为np.ndarray

In [586]: np.array({'test':1})   # a dictionary
Out[586]: array({'test': 1}, dtype=object)

In [587]: np.array(['one','two'])  # a list
Out[587]: 
array(['one', 'two'], 
      dtype='<U3')

In [589]: np.array({'one','two'})  # a set
Out[589]: array({'one', 'two'}, dtype=object)

对于您自己的函数,像这样进行注释:

def foo(x: np.ndarray) -> np.ndarray:

工作。当然,如果您的函数最终调用了某些 numpy 函数,该函数通过 asanyarray 传递其参数(像许多函数一样),这样的注释将不完整,因为输入可能是一个 listnp.matrix 等。


评估此问题和答案时,请注意日期。当时 484 是一个相对较新的 PEP,用于标准 Python 的代码仍在开发中。但看起来提供的链接仍然有效。


1
你正在使用哪个软件、编辑器或解释器来使用 annotations?据我所知,在简单的 Python 3 中,一个函数会得到一个 __annotations__ 字典,但解释器不做任何处理。 - hpaulj
1
您是希望将 typing 注解添加到现有的 numpy 函数(包括 np.array)中,还是只需要简单地在自己的函数中添加注解就能让类型更加容易? - hpaulj
3
我已将此答案标记为被接受的答案,但只是为了完整起见,我想要实现后一种方式(在我的代码中使用类型提示,该代码使用Numpy)。我赞成鸭子类型,但是当您能够提供静态类型信息时,如果仅用于静态代码分析(PyCharm会警告不兼容的类型),我认为您为什么不这样做呢?谢谢,@hpaulj! - Inon
由于typing模块只是提供提示,我创建了两个辅助标签,纯粹是为了可读性,并且请注意它不会通过mypy静态类型检查。 return np_arr.ndim == 1 def Matrix(np_arr): return np_arr.ndim > 1 ```希望能对某些人有所帮助。 - Vitalis
2
形状怎么样?我可以添加类似于 def blah() -> np.ndarray(785): 的提示,但我无法添加第二个维度,例如 -> np.ndarray(785, 10)。拥有形状提示非常有帮助,并为我的代码中产生不同维度数组的多个函数带来了清晰度。 - Steve3p0
1
@Steve3p0,Python中的形状支持正在进行中(https://peps.python.org/pep-0646/),NumPy也是如此(https://github.com/numpy/numpy/issues/16544)。 - APaul

30

在我的公司,我们一直使用:

from typing import TypeVar, Generic, Tuple, Union, Optional
import numpy as np

Shape = TypeVar("Shape")
DType = TypeVar("DType")

class Array(np.ndarray, Generic[Shape, DType]):
    """  
    Use this to type-annotate numpy arrays, e.g. 
        image: Array['H,W,3', np.uint8]
        xy_points: Array['N,2', float]
        nd_mask: Array['...', bool]
    """
    pass

def compute_l2_norm(arr: Array['N,2', float]) -> Array['N', float]:
    return (arr**2).sum(axis=1)**.5

print(compute_l2_norm(arr = np.array([(1, 2), (3, 1.5), (0, 5.5)])))

实际上,我们有一个 MyPy 检查器,可以检查形状是否合适(我们应该在某个时候发布它)。唯一的问题是它不能让 PyCharm 满意(即你仍然会得到令人讨厌的警告线):

enter image description here


1
有关MyPy检查器的任何更新吗?我很想将其集成到我的环境中。 - FarisHijazi
3
这是很好的东西,感谢分享。然而,似乎 nptyping 包(https://github.com/ramonhagenaars/nptyping)在很大程度上进行了泛化。 - amka66
我仍然发现自己在文档编写方面使用这个版本,因为我觉得Array['2,2',int]NDArray[Shape["2, 2"], Int]更容易输入,并且您可以通过命名维度来赋予意义,例如BGRImageArray = Array['H,W,3', 'uint8']清楚地表明第一个维度是高度。尽管如此,如果您确实打算使用mypy进行类型检查,请一定选择nptyping - Peter
这种做法有点“重复造轮子”的感觉(虽然不是很好的轮子),因为已经有了numpy.typing。 - usernumber
@amka66 让人困惑的是,nptyping目前似乎不允许Mypy检查形状不匹配的情况,所以从这个意义上讲,它比这个答案中的解决方案更差,如果你不需要对所有额外的东西(如recarrays)提供支持的话... - smheidrich
@usernumber numpy.typing目前不支持形状注释/检查。而这个解决方案可以实现。 - smheidrich

9

nptyping 增加了许多灵活性,用于指定numpy类型提示。


nptyping改变了我的生活...在输入numpy数组时真的解决了我的问题。当需要验证实例类型、形状等时,它与unittest非常配合。我强烈推荐使用它! - undefined

-2

我所做的就是将其定义为

Dict[Tuple[int, int], TYPE]

例如,如果您想要一个浮点数数组,可以这样做:

a = numpy.empty(shape=[2, 2], dtype=float) # type: Dict[Tuple[int, int], float]

从文档的角度来看,这当然不是完全准确的,但对于分析正确用法并在pyCharm中获得正确的完成效果非常有效!


30
比起使用 np.ndarray 类型,这更糟糕。 - Jules G.M.
1
@JulesG.M.,请问使用np.array和NDArray作为类型有什么区别?如果您有一个快速的答案。 - Jiadong
这是一条旧评论,早在NDArray出现之前@Jiadong。现在NDArray更好了,因为它有工具来指示数组的'dtype'。 - Jules G.M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接