NumPy 1.26 中文官方指南(三)(2)https://developer.aliyun.com/article/1510616
使用 NumPy C-API
- 如何扩展 NumPy
- 编写扩展模块
- 必需子程序
- 定义函数
- 无关键字参数的函数
- 带关键字参数的函数
- 引用计数
- 处理数组对象
- 转换任意序列对象
- 创建全新的 ndarray
- 访问 ndarray 内存和访问 ndarray 元素
- 示例
- 使用 Python 作为粘合剂
- 从 Python 调用其他编译库
- 手动生成的包装器
- f2py
- Cython
- Cython 中的复数加法
- Cython 中的图像滤波
- 结论
- ctypes
- 拥有共享库
- 加载共享库
- 转换参数
- 调用函数
ndpointer
- 完整示例
- 结论
- 您可能会发现有用的其他工具
- SWIG
- SIP
- Boost Python
- PyFort
- 编写自己的 ufunc
- 创建新的通用函数
- 示例非通用函数扩展
- 带一种数据类型的 NumPy ufunc 示例
- 带有多种数据类型的 NumPy ufunc 示例
- 具有多个参数/返回值的示例 NumPy ufunc
- 具有结构化数组数据类型参数的示例 NumPy ufunc
- 超越基础知识
- 在数组中迭代元素
- 基本迭代
- 在除了一个轴之外的所有轴上进行迭代
- 在多个数组上进行迭代
- 在多个数组上进行广播
- 用户定义数据类型
- 添加新数据类型
- 注册强制类型转换函数
- 注册强制类型转换规则
- 注册 ufunc 循环
- 在 C 中对 ndarray 进行子类型化
- 创建子类型
- ndarray 子类型的特定特征
- array_finalize方法
ndarray.__array_finalize__
- array_priority属性
ndarray.__array_priority__
- array_wrap方法
ndarray.__array_wrap__
F2PY 用户指南和参考手册
F2PY
- Fortran 到 Python 接口生成器 - 实用程序的目的是提供 Python 与 Fortran 之间的连接。F2PY 是NumPy (numpy.f2py
)的一部分,也作为一个独立的命令行工具可用。
F2PY 有助于创建/构建使其成为可能的 Python C/API 扩展模块
- 用于调用 Fortran 77/90/95 外部子例程以及 Fortran 90/95 模块子例程以及 C 函数;
- 用于访问 Fortran 77
COMMON
块和 Fortran 90/95 模块数据,包括可分配数组
从 Python。
F2PY 可以作为命令行工具f2py
或作为一个 Python 模块numpy.f2py
来使用。虽然我们尝试将命令行工具作为 numpy 设置的一部分提供,但像 Windows 这样的某些平台很难可靠地将可执行文件放在PATH
上。如果您的系统中没有f2py
命令可用,您可能需要将其作为模块运行:
python -m numpy.f2py
如果您运行f2py
而没有参数,并且最后一行的numpy 版本
与从python -m numpy.f2py
打印的 NumPy 版本匹配,则可以使用较短的版本。如果不是这样,或者无法运行f2py
,则应该将本指南中提到的所有对f2py
的调用替换为较长的版本。
- 包装的三种方式 - 入门指南
- 快速的方式
- 明智的做法
- 快速又聪明的方式
- F2PY 用户指南
- 包装的三种方式 - 入门指南
- 快速的方式
- 明智的做法
- 快速又聪明的方式
- 使用 F2PY
- 使用
f2py
作为命令行工具 - Python 模块
numpy.f2py
- 自动生成扩展模块
- F2PY 示例
- F2PY 演练:一个基本的扩展模块
- 一个过滤示例
depends
关键字示例- 阅读更多
- F2PY 参考手册
- 签名文件
- 签名文件语法
- 在 Python 中使用 F2PY 绑定
- Fortran 类型对象
- 标量参数
- 字符串参数
- 数组参数
- 回调参数
- 公共块
- Fortran 90 模块数据
- 可分配数组
- F2PY 和构建系统
- 基本概念
- 构建系统
- 高级 F2PY 使用情况
- 向 F2PY 生成的模块添加用户定义函数
- 添加用户定义变量
- 处理 KIND 规范
- 字符字符串
- F2PY 测试套件
- 添加一个测试
- 使用 F2PY
- 将
f2py
作为命令行工具使用
- 1. 签名文件生成
- 2. 扩展模块构建
- 3. 构建一个模块
- 其他选项
- Python 模块
numpy.f2py
compile
get_include
run_main
- 自动生成扩展模块
- 在 Python 中使用 F2PY 绑定
- Fortran 类型对象
- 标量参数
- 字符串参数
- 数组参数
- 回调参数
- 解析回调函数的参数
- 公共块
- Fortran 90 模块数据
- 可分配数组
- 签名文件
- 签名文件语法
- Python 模块块
- Fortran/C 例程签名
- 类型声明
- 语句
- 属性
- 扩展
- 扩展的字符选择器
- F2PY 和构建系统
- 基本概念
- 构建系统
- 通过
numpy.distutils
使用 - 通过
meson
使用 - 通过
cmake
使用 - 通过
scikit-build
使用
- 高级 F2PY 使用情况
- 向 F2PY 生成的模块添加用户定义函数
- 添加用户定义变量
- 处理 KIND 指定符
- 字符字符串
- 假定长度的字符字符串
- F2PY 和 Windows
- 概述
- 基准线
- Powershell 和 MSVC
- Windows 商店 Python 路径
- F2PY 和 Windows Intel Fortran
- F2PY 和 MSYS2 上的 Windows
- F2PY 和 Conda on Windows
- F2PY 和 PGI Fortran on Windows
开发人员的底层文档
这些文档旨在深入了解 NumPy,面向开发人员。
- NumPy 数组的内部组织
- NumPy C 代码解释
- 内存对齐
- 字节交换
- 编写自定义数组容器
- 子类化 ndarray
与 NumPy 的互操作性
NumPy 的 ndarray 对象提供了对数组结构化数据进行操作的高级 API,以及基于 分块内存中存储 的 API 的具体实现。虽然这个 API 功能强大且相当通用,但它的具体实现有限制。随着数据集的增长和 NumPy 在各种新环境和架构中的使用,有些情况下分块内存中存储策略不适用,这导致不同的库为其自己的用途重新实现了这个 API。这包括 GPU 数组 (CuPy)、稀疏数组 (scipy.sparse
、PyData/Sparse) 和并行数组 (Dask 数组),以及深度学习框架中类似 NumPy 的实现,如 TensorFlow 和 PyTorch。同样,还有许多项目建立在 NumPy API 之上,用于标记和索引数组 (XArray)、自动微分 (JAX)、遮罩数组 (numpy.ma
)、物理单位 (astropy.units、pint、unyt) 等等,这些项目在 NumPy API 的基础上添加了额外的功能。
然而,用户仍然希望使用熟悉的 NumPy API 和最小(理想情况下为零)的移植开销重新使用现有代码来处理这些数组。考虑到这一目标,为具有与 NumPy 匹配的高级 API 的多维数组实现定义了各种协议。
广义上来说,用于与 NumPy 互操作的特性分为三组:
- 将外部对象转换为 ndarray 的方法;
- 将执行延迟从 NumPy 函数转移到另一个数组库的方法;
- 使用 NumPy 函数并返回外部对象实例的方法。
我们在下面描述这些特性。
1. 在 NumPy 中使用任意对象
NumPy API 的第一组互操作特性允许在可能的情况下将外部对象视为 NumPy 数组。当 NumPy 函数遇到外部对象时,它们会依次尝试:
- 缓冲区协议,在 Python C-API 文档 中描述。
__array_interface__
协议,描述在 此页面 中。作为 Python 缓冲区协议的前身,它定义了一种从其他 C 扩展中访问 NumPy 数组内容的方法。__array__()
方法,用于要求任意对象将自身转换为数组。
对于缓冲区和__array_interface__
协议,对象描述其内存布局,NumPy 会完成其他一切(如果可能的话,就是零拷贝)。如果这不可能,那么对象本身负责从__array__()
返回一个ndarray
。
DLPack是用于以一种语言和设备不可知的方式将外部对象转换为 NumPy 数组的另一种协议。NumPy 不会使用 DLPack 隐式地将对象转换为 ndarrays。它提供了函数numpy.from_dlpack
,该函数接受任何实现__dlpack__
方法的对象,并输出一个 NumPy ndarray(通常是输入对象的数据缓冲区的视图)。DLPack 的 Python 规范页面详细解释了__dlpack__
协议。
数组接口协议
数组接口协议定义了一种让类似数组对象重新使用彼此的数据缓冲区的方式。其实现依赖于以下属性或方法的存在:
__array_interface__
:包含数组样对象的形状、元素类型,可选的数据缓冲区地址和步长的 Python 字典;__array__()
: 返回数组样对象的 NumPy ndarray 视图的方法;
可以直接检查__array_interface__
属性:
>>> import numpy as np >>> x = np.array([1, 2, 5.0, 8]) >>> x.__array_interface__ {'data': (94708397920832, False), 'strides': None, 'descr': [('', '<f8')], 'typestr': '<f8', 'shape': (4,), 'version': 3}
__array_interface__
属性还可以用于就地操纵对象数据:
>>> class wrapper(): ... pass ... >>> arr = np.array([1, 2, 3, 4]) >>> buf = arr.__array_interface__ >>> buf {'data': (140497590272032, False), 'strides': None, 'descr': [('', '<i8')], 'typestr': '<i8', 'shape': (4,), 'version': 3} >>> buf['shape'] = (2, 2) >>> w = wrapper() >>> w.__array_interface__ = buf >>> new_arr = np.array(w, copy=False) >>> new_arr array([[1, 2], [3, 4]])
我们可以检查arr
和new_arr
是否共享相同的数据缓冲区:
>>> new_arr[0, 0] = 1000 >>> new_arr array([[1000, 2], [ 3, 4]]) >>> arr array([1000, 2, 3, 4])
__array__()
方法
__array__()
方法确保任何类似于 NumPy 的对象(数组,任何公开数组接口的对象,其__array__()
方法返回数组或任何嵌套序列的对象)实现它都可以用作 NumPy 数组。如果可能,这意味着使用__array__()
来创建数组样对象的 NumPy ndarray 视图。否则,这将复制数据到一个新的 ndarray 对象中。这并不是最佳选择,因为强制将数组强制转换为 ndarrays 可能会导致性能问题,或者需要复制和丢失元数据,原始对象以及原始对象可能具有的任何属性/行为都会丢失。
要查看包括使用__array__()
的自定义数组实现的示例,请参见编写自定义数组容器。
DLPack 协议
DLPack协议定义了跨 strided n 维数组对象的内存布局。它提供以下语法以进行数据交换:
- 一个
numpy.from_dlpack
函数,它接受带有__dlpack__
方法的(数组)对象,并使用该方法来构建包含来自x
的数据的新数组。 - 数组对象上的
__dlpack__(self, stream=None)
和__dlpack_device__
方法,它们将在from_dlpack
中调用,以查询数组所在的设备(在多个 GPU 的情况下可能需要传入正确的流),以及访问数据。
与缓冲区协议不同,DLPack 允许交换包含非 CPU 设备(例如 Vulkan 或 GPU)上数据的数组。由于 NumPy 仅支持 CPU,它只能转换数据存在于 CPU 上的对象。但其他库,如PyTorch和 CuPy,可能使用该协议在 GPU 上交换数据。
2. 在不转换的情况下操作外部对象
NumPy API 定义的第二组方法允许我们将执行从 NumPy 函数延迟到另一个数组库。
考虑以下函数。
>>> import numpy as np >>> def f(x): ... return np.mean(np.exp(x))
请注意,np.exp
是一个 ufunc,这意味着它以逐元素的方式作用于 ndarrays。另一方面,np.mean
沿着数组的一个轴进行操作。
我们可以将f
直接应用于一个 NumPy ndarray 对象:
>>> x = np.array([1, 2, 3, 4]) >>> f(x) 21.1977562209304
我们希望这个函数能够在任何类似于 NumPy 的数组对象上同样有效。
NumPy 允许类通过以下接口指示它希望以自定义方式进行计算:
__array_ufunc__
: 允许第三方对象支持和覆盖 ufuncs。__array_function__
: 用于覆盖通用函数中不涵盖的 NumPy 功能的默认操作。
只要外部对象实现了__array_ufunc__
或__array_function__
协议,就可以在它们上操作而无需进行显式转换。
__array_ufunc__
协议
通用函数(或简写为 ufunc)是一个对函数进行“矢量化”封装的函数,它接受固定数量的特定输入并产生固定数量的特定输出。如果非 ndarray 对象的输入定义了__array_ufunc__
方法,则控制完全传递给该函数,即 ufunc 将被覆盖。在该(非 ndarray)对象上定义的__array_ufunc__
方法可以访问 NumPy ufunc。由于 ufunc 具有明确定义的结构,外部__array_ufunc__
方法可能依赖 ufunc 属性,如.at()
、.reduce()
等。
子类可以通过覆盖默认的ndarray.__array_ufunc__
方法来覆盖在其上执行 NumPy ufuncs 时发生的情况。这个方法将代替 ufunc 的执行,并且应该返回操作的结果,或者如果请求的操作未实现,则返回NotImplemented
。
__array_function__
协议
为了足够覆盖 NumPy API 以支持下游项目,需要超越 __array_ufunc__
并实现一个协议,允许 NumPy 函数的参数控制并将执行转移到另一个函数(例如 GPU 或并行实现),以安全和一致的方式跨项目进行。
__array_function__
的语义与__array_ufunc__
非常相似,只是操作由任意可调用对象指定,而不是由 ufunc 实例和方法指定。更多细节,请参见NEP 18 — NumPy 高级数组函数的调度机制,其中包含 NumPy Enhancement Proposals。"
3. 返回外部对象
第三种特性集旨在使用 NumPy 函数实现,然后将返回值转换为外部对象的实例。__array_finalize__
和 __array_wrap__
方法在幕后起作用,以确保可以根据需要指定 NumPy 函数的返回类型。
__array_finalize__
方法是 NumPy 提供的机制,允许子类处理新实例被创建的各种方式。每当系统从 ndarray 的子类(子类型)内部分配新数组时,都会调用此方法。它可以用于在构建后更改属性,或从“父类”更新元信息。
__array_wrap__
方法“包装了行动”,意思是允许任何对象(如用户定义的函数)设置其返回值的类型并更新属性和元数据。这可以被视为__array__
方法的相反。在每个实现__array_wrap__
的对象的最高数组优先级或指定的输出对象之后,将对输入对象调用此方法。 __array_priority__
属性用于确定在返回对象的 Python 类型存在多种可能性的情况下要返回什么类型的对象。例如,子类可以选择使用此方法将输出数组转换为子类的实例,并在将数组返回给用户之前更新元数据。
有关这些方法的更多信息,请参阅 ndarray 子类化 和 ndarray 子类型的特定特性。
互操作性示例
示例:Pandas Series
对象
考虑以下内容:
>>> import pandas as pd >>> ser = pd.Series([1, 2, 3, 4]) >>> type(ser) pandas.core.series.Series
现在,ser
不是一个 ndarray,但因为它实现了 array_ufunc 协议,我们可以将 ufunc 应用于它,好像它是一个 ndarray 一样:
>>> np.exp(ser) 0 2.718282 1 7.389056 2 20.085537 3 54.598150 dtype: float64 >>> np.sin(ser) 0 0.841471 1 0.909297 2 0.141120 3 -0.756802 dtype: float64
我们甚至可以与其他 ndarray 执行操作:
>>> np.add(ser, np.array([5, 6, 7, 8])) 0 6 1 8 2 10 3 12 dtype: int64 >>> f(ser) 21.1977562209304 >>> result = ser.__array__() >>> type(result) numpy.ndarray
示例:PyTorch 张量
PyTorch 是一个针对使用 GPU 和 CPU 进行深度学习的优化张量库。PyTorch 数组通常被称为 张量。张量类似于 NumPy 的 ndarrays,只是张量可以在 GPU 或其他硬件加速器上运行。事实上,张量和 NumPy 数组通常可以共享相同的底层内存,消除了复制数据的需要。
>>> import torch >>> data = [[1, 2],[3, 4]] >>> x_np = np.array(data) >>> x_tensor = torch.tensor(data)
请注意 x_np
和 x_tensor
是不同种类的对象:
>>> x_np array([[1, 2], [3, 4]]) >>> x_tensor tensor([[1, 2], [3, 4]])
然而,我们可以将 PyTorch 张量视为 NumPy 数组,而无需显式转换:
>>> np.exp(x_tensor) tensor([[ 2.7183, 7.3891], [20.0855, 54.5982]], dtype=torch.float64)
此外,请注意该函数的返回类型与初始数据类型兼容。
警告
尽管将 ndarrays 和张量混合使用可能很方便,但不建议这样做。它对于非 CPU 张量不起作用,在一些边缘情况下会有意外的行为。用户应该优先显式地将 ndarray 转换为张量。
注意
PyTorch 没有实现 __array_function__
或 __array_ufunc__
。在底层,Tensor.__array__()
方法返回张量数据缓冲区的 NumPy ndarray 视图。详情请参阅 此问题 和 torch_function 实现。
还需注意,即使 torch.Tensor
不是 ndarray 的子类,我们也可以看到 __array_wrap__
在这里发挥作用:
>>> import torch >>> t = torch.arange(4) >>> np.abs(t) tensor([0, 1, 2, 3])
PyTorch 实现了 __array_wrap__
来能够从 NumPy 函数中获取张量,并且我们可以直接修改它以控制从这些函数中返回哪种类型的对象。
例如:CuPy 数组
CuPy 是一个用于 GPU 加速计算的 NumPy/SciPy 兼容数组库。CuPy 通过实现 cupy.ndarray
实现了 NumPy 接口的子集,与 NumPy ndarrays 对应。
>>> import cupy as cp >>> x_gpu = cp.array([1, 2, 3, 4])
cupy.ndarray
对象实现了 __array_ufunc__
接口。这使得可以将 NumPy ufuncs 应用于 CuPy 数组(这将推迟操作到与 ufunc 匹配的 CuPy CUDA/ROCm 实现):
>>> np.mean(np.exp(x_gpu)) array(21.19775622)
请注意这些操作的返回类型仍与初始类型一致:
>>> arr = cp.random.randn(1, 2, 3, 4).astype(cp.float32) >>> result = np.sum(arr) >>> print(type(result)) <class 'cupy._core.core.ndarray'>
请参阅 CuPy 文档中的此页面以获取详细信息。
cupy.ndarray
也实现了 __array_function__
接口,这意味着可以执行诸如
>>> a = np.random.randn(100, 100) >>> a_gpu = cp.asarray(a) >>> qr_gpu = np.linalg.qr(a_gpu)
CuPy 在 cupy.ndarray
对象上实现了许多 NumPy 函数,但并非全部。详情请参阅 CuPy 文档。
例如:Dask 数组
Dask 是 Python 中用于并行计算的灵活库。Dask Array 使用分块算法实现了 NumPy ndarray 接口的子集,将大数组切分成许多小数组。这允许使用多个核心对大于内存的数组进行计算。
Dask 支持 __array__()
和 __array_ufunc__
。
>>> import dask.array as da >>> x = da.random.normal(1, 0.1, size=(20, 20), chunks=(10, 10)) >>> np.mean(np.exp(x)) dask.array<mean_agg-aggregate, shape=(), dtype=float64, chunksize=(), chunktype=numpy.ndarray> >>> np.mean(np.exp(x)).compute() 5.090097550553843
注意
Dask 是惰性评估的,只有在通过调用 compute()
请求时,才会计算计算结果。
详细了解 Dask 数组文档 和 Dask 数组与 NumPy 数组互操作性的范围。
示例:DLPack
几个 Python 数据科学库实现了 __dlpack__
协议。其中包括 PyTorch 和 CuPy。可以在 DLPack 文档的这一页 找到实现此协议的库的完整列表。
将 PyTorch CPU 张量转换为 NumPy 数组:
>>> import torch >>> x_torch = torch.arange(5) >>> x_torch tensor([0, 1, 2, 3, 4]) >>> x_np = np.from_dlpack(x_torch) >>> x_np array([0, 1, 2, 3, 4]) >>> # note that x_np is a view of x_torch >>> x_torch[1] = 100 >>> x_torch tensor([ 0, 100, 2, 3, 4]) >>> x_np array([ 0, 100, 2, 3, 4])
导入的数组是只读的,因此无法进行写入或原地操作:
>>> x.flags.writeable False >>> x_np[1] = 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: assignment destination is read-only
为了原地操作导入的数组,必须创建副本,但这将意味着复制内存。对于非常大的数组,请不要这样做:
>>> x_np_copy = x_np.copy() >>> x_np_copy.sort() # works
注意
请注意,由于 NumPy 不支持 GPU 设备,无法将 GPU 张量转换为 NumPy 数组:
>>> x_torch = torch.arange(5, device='cuda') >>> np.from_dlpack(x_torch) Traceback (most recent call last): File "<stdin>", line 1, in <module> RuntimeError: Unsupported device in DLTensor.
但是,如果两个库都支持数据缓冲区所在的设备,则可以使用 __dlpack__
协议(例如 PyTorch 和 CuPy):
>>> x_torch = torch.arange(5, device='cuda') >>> x_cupy = cupy.from_dlpack(x_torch)
类似地,可以将 NumPy 数组转换为 PyTorch 张量:
>>> x_np = np.arange(5) >>> x_torch = torch.from_dlpack(x_np)
只读数组无法导出:
>>> x_np = np.arange(5) >>> x_np.flags.writeable = False >>> torch.from_dlpack(x_np) Traceback (most recent call last): File "<stdin>", line 1, in <module> File ".../site-packages/torch/utils/dlpack.py", line 63, in from_dlpack dlpack = ext_tensor.__dlpack__() TypeError: NumPy currently only supports dlpack for writeable arrays
进一步阅读
- 数组接口协议
- 编写自定义数组容器
- 特殊属性和方法(关于
__array_ufunc__
和__array_function__
协议的详细信息) - 子类化 ndarray(关于
__array_wrap__
和__array_finalize__
方法的详细信息) - ndarray 子类型化的特定功能(有关
__array_finalize__
、__array_wrap__
和__array_priority__
实现的更多详细信息) - NumPy 路线图:互操作性
- PyTorch 与 NumPy 桥接的文档
1. 在 NumPy 中使用任意对象
NumPy API 的第一组互操作性功能允许在可能的情况下将外部对象视为 NumPy 数组。当 NumPy 函数遇到外部对象时,它们将按顺序尝试:
- 缓冲区协议,在 Python C-API 文档中 有描述。
__array_interface__
协议,在 这个页面 有描述。作为 Python 缓冲区协议的前身,它定义了从其他 C 扩展中访问 NumPy 数组内容的方法。__array__()
方法,请求任意对象将自身转换为数组。
对于缓冲区和 __array_interface__
协议,对象描述其内存布局,NumPy 执行其他所有操作(如果可能,零拷贝)。 如果不可能,则对象本身负责从 __array__()
返回 ndarray
。
DLPack 是将外部对象以一种与语言和设备无关的方式转换为 NumPy 数组的另一种协议。 NumPy 不会使用 DLPack 将对象隐式转换为 ndarrays。 它提供了函数 numpy.from_dlpack
,该函数接受实现 __dlpack__
方法的任何对象,并输出 NumPy ndarray(通常是输入对象数据缓冲区的视图)。 DLPack 的 Python 规范 页面详细说明了 __dlpack__
协议。
数组接口协议
数组接口协议 定义了数组样对象重用彼此数据缓冲区的方式。 其实现依赖于以下属性或方法的存在:
__array_interface__
:一个 Python 字典,包含类似数组对象的形状、元素类型,以及可选的数据缓冲地址和步幅;__array__()
:返回类似数组对象的 NumPy ndarray 视图的方法;
可以直接检查 __array_interface__
属性:
>>> import numpy as np >>> x = np.array([1, 2, 5.0, 8]) >>> x.__array_interface__ {'data': (94708397920832, False), 'strides': None, 'descr': [('', '<f8')], 'typestr': '<f8', 'shape': (4,), 'version': 3}
__array_interface__
属性还可用于就地操作对象数据:
>>> class wrapper(): ... pass ... >>> arr = np.array([1, 2, 3, 4]) >>> buf = arr.__array_interface__ >>> buf {'data': (140497590272032, False), 'strides': None, 'descr': [('', '<i8')], 'typestr': '<i8', 'shape': (4,), 'version': 3} >>> buf['shape'] = (2, 2) >>> w = wrapper() >>> w.__array_interface__ = buf >>> new_arr = np.array(w, copy=False) >>> new_arr array([[1, 2], [3, 4]])
我们可以检查 arr
和 new_arr
是否共享相同的数据缓冲区:
>>> new_arr[0, 0] = 1000 >>> new_arr array([[1000, 2], [ 3, 4]]) >>> arr array([1000, 2, 3, 4])
__array__()
方法
__array__()
方法确保任何类似 NumPy 的对象(数组、公开数组接口的任何对象、其 __array__()
方法返回数组的对象或任何嵌套序列),只要实现它,就可以用作 NumPy 数组。 如果可能,这将意味着使用 __array__()
来创建数组对象的 NumPy ndarray 视图。 否则,这将复制数据到一个新的 ndarray 对象中。 这不是最佳的,因为将数组强制转换为 ndarrays 可能会导致性能问题或创建副本和元数据丢失,因为原始对象及其可能具有的任何属性/行为都会丢失。
要查看自定义数组实现的示例,包括使用 __array__()
的用法,请参见 编写自定义数组容器。
DLPack 协议
DLPack 协议定义了分块的 n 维数组对象的内存布局。 它为数据交换提供了以下语法:
numpy.from_dlpack
函数接受具有__dlpack__
方法的(数组)对象,并使用该方法构造一个包含来自x
的数据的新数组。- 数组对象上的
__dlpack__(self, stream=None)
和__dlpack_device__
方法将从from_dlpack
中调用,以查询数组所在的设备(可能需要传递正确的流,例如在多个 GPU 的情况下),并访问数据。
与缓冲区协议不同,DLPack 允许交换包含在 CPU 之外设备上的数据的数组(例如 Vulkan 或 GPU)。由于 NumPy 仅支持 CPU,因此它只能转换其数据存在于 CPU 的对象。但其他库,如PyTorch和CuPy,可以使用这个协议在 GPU 上交换数据。
NumPy 1.26 中文官方指南(三)(4)https://developer.aliyun.com/article/1510619