类型标注 (numpy.typing
)#
1.20 版本新增。
NumPy API 的大部分都具有 PEP 484 风格的类型标注。此外,还有许多类型别名可供用户使用,其中最突出的是以下两种:
Mypy 插件#
1.21 版本新增。
一个 mypy 插件,用于管理多项平台特定标注。其功能可分为三个独立部分:
分配某些
number
子类的(依赖于平台的)精度,包括int_
、intp
和longlong
等。有关受影响类的全面概述,请参阅标量类型文档。如果没有此插件,所有相关类的精度都将被推断为Any
。移除所有在当前平台上不可用的扩展精度
number
子类。最值得注意的是,这包括float128
和complex256
等。如果没有此插件,就 mypy 而言,所有扩展精度类型都将对所有平台可用。分配
c_intp
的(依赖于平台的)精度。如果没有此插件,该类型将默认为ctypes.c_int64
。1.22 版本新增。
自 2.3 版本起弃用。
示例#
要启用此插件,必须将其添加到 mypy 配置文件中
[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 代码,但类型检查器会对此发出警告,因为不鼓励使用此方式。请参阅:数据类型对象
数值精度#
`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
、float32
和 float64
等仍然是 floating
的子类型,但与运行时不同,它们不一定被视为子类。
Timedelta64#
在静态类型检查时,timedelta64
类不被视为 signedinteger
的子类,前者仅继承自 generic
。
0 维数组#
在运行时,NumPy 会积极地将任何传入的 0 维数组转换为其对应的 generic
实例。在引入形状类型(参见 PEP 646)之前,不幸的是无法区分 0 维数组和大于 0 维的数组。虽然这不完全正确,但所有可能执行 0 维数组 -> 标量转换的操作目前都被标注为仅返回一个 ndarray
。
如果预先已知某操作 *将* 执行 0 维数组 -> 标量转换,则可以考虑使用 typing.cast
或 # type: ignore
注释手动纠正这种情况。
记录数组 dtypes#
numpy.recarray
的 dtype,以及通常的 创建记录数组 函数,可以通过以下两种方式之一指定:
直接通过
dtype
参数。通过
numpy.rec.format_parser
运行,最多可有五个辅助参数:formats
、names
、titles
、aligned
和byteorder
。
这两种方法目前被类型化为互斥的,即如果指定了 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[...]#
-
其中包括:
1.20 版本新增。
另请参见
- 指定和构造数据类型
所有可强制转换为数据类型的对象的全面概述。
示例
>>> 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.type
而言是泛型的。可在运行时用于为具有给定 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[source]#
一种在静态类型检查期间表示
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*]