类型标注 ( numpy.typing )#

在 1.20 版本加入.

NumPy API 的大部分都有 PEP 484 风格的类型标注.此外,还为用户提供了一些类型别名,其中最突出的两个是:

Mypy 插件#

在 1.21 版本加入.

用于管理许多平台特定注解的 mypy 插件. 它的功能可以分为三个不同的部分:

  • 指定某些 number 子类(包括 int_ , intplonglong 等)的(平台相关的)精度. 有关受影响类的全面概述,请参阅有关 scalar types 的文档. 如果没有插件,所有相关类的精度将被推断为 Any .

  • 删除所有不可用于该平台的扩展精度 number 子类. 最值得注意的是,这包括 float128complex256 等. 如果没有插件,就 mypy 而言,所有扩展精度类型都将可用于所有平台.

  • 分配 c_intp 的(平台相关的)精度. 如果没有插件,该类型将默认为 ctypes.c_int64 .

    在 1.22 版本加入.

自 2.3 版本弃用.

示例#

要启用插件,必须将其添加到 mypy configuration file 中:

[mypy]
plugins = numpy.typing.mypy_plugin

与运行时 NumPy API 的差异#

NumPy 非常灵活. 尝试静态地描述所有可能性将导致类型不是很有帮助. 因此,类型化的NumPy API 通常比运行时NumPy API 更严格. 本节介绍一些值得注意的差异.

ArrayLike#

ArrayLike 类型尝试避免创建对象数组. 例如,

>>> np.array(x**2 for x in range(10))
array(<generator object <genexpr> at ...>, dtype=object)

是有效的 NumPy 代码,它将创建一个 0 维对象数组. 但是,当使用 NumPy 类型时,类型检查器会抱怨上面的示例. 如果你真的打算这样做,那么你可以使用 # type: ignore 注释:

>>> np.array(x**2 for x in range(10))  # type: ignore

或者显式地将数组类对象键入为 Any :

>>> from typing import Any
>>> array_like: Any = (x**2 for x in range(10))
>>> np.array(array_like)
array(<generator object <genexpr> at ...>, dtype=object)

ndarray#

可以在运行时改变数组的 dtype. 例如,以下代码有效:

>>> x = np.array([1, 2])
>>> x.dtype = np.bool

类型不允许这种突变. 想要编写静态类型代码的用户应该使用 numpy.ndarray.view 方法来创建具有不同 dtype 的数组视图.

DTypeLike#

DTypeLike 类型尝试避免使用字段字典来创建 dtype 对象,如下所示:

>>> x = np.dtype({"field1": (float, 1), "field2": (int, 3)})

虽然这是有效的 NumPy 代码,但类型检查器会对此提出异议,因为不鼓励使用它. 请参阅: Data type objects

数字精度#

numpy.number 子类的精度被视为不变的泛型参数(参见 NBitBase ),从而简化了涉及基于精度的转换的过程的注释.

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

>>> T = TypeVar("T", bound=npt.NBitBase)
>>> def func(a: "np.floating[T]", b: "np.floating[T]") -> "np.floating[T]":
...     ...

因此,类似 float16 , float32float64 仍然是 floating 的子类型,但是,与运行时相反,它们不一定被视为子类.

Timedelta64#

在静态类型检查时, timedelta64 类不被认为是 signedinteger 的子类,前者仅从 generic 继承.

0D 数组#

在运行时,numpy 会积极地将任何传递的 0D 数组转换为它们对应的 generic 实例. 在引入形状类型(参见 PEP 646 )之前,不幸的是,无法在 0D 和 >0D 数组之间进行必要的区分. 因此,虽然不是完全正确,但目前所有可能执行 0D 数组 -> 标量转换的操作都被注释为专门返回 ndarray .

如果预先知道某个操作将执行 0D 数组到标量的转换,那么可以考虑使用 typing.cast# type: ignore 注释手动解决这种情况.

记录数组数据类型#

numpy.recarray 的 dtype,以及通常的 创建记录数组 函数,可以通过两种方式指定:

  • 直接通过 dtype 参数.

  • 最多五个辅助参数,通过 numpy.rec.format_parser 运行: formats , names , titles , alignedbyteorder .

这两种方法目前被类型化为互斥的,即如果指定了 dtype ,则不能指定 formats .虽然在运行时没有(严格)强制执行这种互斥性,但组合两个 dtype 说明符可能会导致意外甚至完全错误的行为.

API#

numpy.typing.ArrayLike = typing.Union[...]#

一个 Union ,表示可以强制转换为 ndarray 的对象.

其中包括:

  • 标量.

  • (嵌套)序列.

  • 实现 __array__ 协议的对象.

在 1.20 版本加入.

参见

array_like :

任何可以解释为 ndarray 的标量或序列.

示例

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

>>> def as_array(a: npt.ArrayLike) -> np.ndarray:
...     return np.array(a)
numpy.typing.DTypeLike = typing.Union[...]#

一个 Union ,表示可以强制转换为 dtype 的对象.

其中包括:

  • type 对象.

  • 字符代码或 type 对象的名称.

  • 具有 .dtype 属性的对象.

在 1.20 版本加入.

参见

Specifying and constructing data types

可以强制转换为数据类型的所有对象的全面概述.

示例

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

>>> def as_dtype(d: npt.DTypeLike) -> np.dtype:
...     return np.dtype(d)
numpy.typing.NDArray = numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[~_ScalarT]]#

一个 np.ndarray[tuple[Any, ...], np.dtype[ScalarT]] 类型别名,关于其 dtype.typegeneric .

可以在运行时用于对具有给定 dtype 和未指定形状的数组进行类型标注.

在 1.21 版本加入.

示例

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

>>> print(npt.NDArray)
numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[~_ScalarT]]

>>> print(npt.NDArray[np.float64])
numpy.ndarray[tuple[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)
class numpy.typing.NBitBase[源代码]#

一个表示静态类型检查期间 numpy.number 精度的类型.

NBitBase 专门用于静态类型检查,表示分层子类的基础.此处的每个后续子类都用于表示较低级别的精度,例如 64Bit > 32Bit > 16Bit .

在 1.20 版本加入.

自 2.3 版本弃用: 请改用 @typing.overload 或以标量类型作为上界的 TypeVar .

示例

下面是一个典型的用法示例:NBitBase 在此用于注释一个函数,该函数接受任意精度的浮点数和整数作为参数,并返回精度最高的新的浮点数(例如 np.float16 + np.int64 -> np.float64 ).

>>> from typing import TypeVar, TYPE_CHECKING
>>> import numpy as np
>>> import numpy.typing as npt

>>> S = TypeVar("S", bound=npt.NBitBase)
>>> T = TypeVar("T", bound=npt.NBitBase)

>>> def add(a: np.floating[S], b: np.integer[T]) -> np.floating[S | T]:
...     return a + b

>>> a = np.float16()
>>> b = np.int64()
>>> out = add(a, b)

>>> if TYPE_CHECKING:
...     reveal_locals()
...     # note: Revealed local types are:
...     # note:     a: numpy.floating[numpy.typing._16Bit*]
...     # note:     b: numpy.signedinteger[numpy.typing._64Bit*]
...     # note:     out: numpy.floating[numpy.typing._64Bit*]