类型提示 (numpy.typing)#

版本 1.20 中的新功能。

NumPy API 的大部分内容都具有 PEP 484 样式的类型标注。此外,还提供了一些类型别名供用户使用,其中最突出的是以下两个

Mypy 插件#

版本 1.21 中的新功能。

一个用于管理一些平台特定标注的 mypy 插件。其功能可以分为三个不同的部分

  • 为某些 number 子类的(平台相关的)精度赋值,包括 int_intplonglong 等。有关受影响类的全面概述,请参阅有关 标量类型 的文档。如果没有插件,所有相关类的精度都将被推断为 Any

  • 移除所有在相关平台上不可用的扩展精度 number 子类。最值得注意的是,这包括 float128complex256 等。如果没有插件,在 mypy 看来,所有扩展精度类型都对所有平台可用。

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

    版本 1.22 中的新功能。

示例#

要启用插件,必须将其添加到 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]":
...     ...

因此,float16float32float64 仍然是 floating 的子类型,但是,与运行时相反,它们不一定会被视为子类。

Timedelta64#

timedelta64 类不被视为 signedinteger 的子类,在进行静态类型检查时,前者仅继承自 generic

0D 数组#

在运行时,numpy 会积极地将任何传递的 0D 数组强制转换为其对应的 generic 实例。在引入形状类型之前(请参阅 PEP 646),不幸的是无法在 0D 和 >0D 数组之间进行必要的区分。虽然这并不完全正确,但所有可能执行 0D 数组 -> 标量强制转换的操作当前都标注为仅返回 ndarray

如果事先知道某个操作执行 0D 数组 -> 标量强制转换,则可以考虑使用 typing.cast# type: ignore 注释手动解决此问题。

记录数组 dtype#

numpy.recarray 的 dtype 和通常的 创建记录数组 函数可以通过以下两种方式之一指定

  • 直接通过 dtype 参数。

  • 使用最多五个辅助参数,这些参数通过 numpy.rec.format_parser 操作:formatsnamestitlesalignedbyteorder

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

API#

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

表示可强制转换为 ndarray 的对象的 Union

其中包括:

  • 标量。

  • (嵌套)序列。

  • 实现 __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[...]#

表示可强制转换为 dtype 的对象的 Union

其中包括:

  • type 对象。

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

  • 具有 .dtype 属性的对象。

版本 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[typing.Any, numpy.dtype[+_ScalarType_co]]#

np.ndarray[Any, np.dtype[+ScalarType]] 类型别名,关于其 dtype.type泛型

可以在运行时用于为具有给定 dtype 和未指定形状的数组进行类型提示。

版本 1.21 中的新功能。

示例

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

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

>>> 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)
class numpy.typing.NBitBase[source]#

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

仅用于静态类型检查的目的,NBitBase 表示一组分层子类的基类。每个后续子类在此处用于表示更低的精度级别,例如64Bit > 32Bit > 16Bit

版本 1.20 中的新功能。

示例

下面是一个典型的用法示例:NBitBase 在此处用于标注一个函数,该函数以任意精度的浮点数和整数作为参数,并返回一个新的浮点数,其精度为两者中最大的(例如np.float16 + np.int64 -> np.float64)。

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

>>> T1 = TypeVar("T1", bound=npt.NBitBase)
>>> T2 = TypeVar("T2", bound=npt.NBitBase)

>>> def add(a: np.floating[T1], b: np.integer[T2]) -> np.floating[T1 | T2]:
...     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*]