ufunc API#

常量#

UFUNC_{THING}_{ERR}
UFUNC_FPE_DIVIDEBYZERO#
UFUNC_FPE_OVERFLOW#
UFUNC_FPE_UNDERFLOW#
UFUNC_FPE_INVALID#
PyUFunc_{VALUE}
PyUFunc_One#
PyUFunc_Zero#
PyUFunc_MinusOne#
PyUFunc_ReorderableNone#
PyUFunc_None#
PyUFunc_IdentityValue#

#

NPY_LOOP_BEGIN_THREADS#

在通用函数代码中使用,只有在 loop->obj 不为真时才释放 Python GIL(即这不是一个 OBJECT 数组循环).需要使用 NPY_BEGIN_THREADS_DEF 在变量声明区域中.

NPY_LOOP_END_THREADS#

在通用函数代码中使用,如果之前释放了Python GIL(因为loop->obj不为真),则重新获取它.

类型#

type PyUFuncGenericFunction#

指向实际实现基础(逐元素)函数的函数指针 \(N\) 次,具有以下签名:

void loopfunc(char **args, npy_intp const *dimensions, npy_intp const *steps, void *data)#
参数:
  • args – 指向输入和输出数组实际数据的指针数组.首先给出输入参数,然后是输出参数.

  • dimensions – 指向此函数正在循环的维度大小的指针.

  • steps – 指向每个输入和输出参数在该维度中获取下一个元素所需跳转的字节数的指针.

  • data – 可以与 ufunc 一起存储的任意数据(额外参数、函数名称等),在调用时会传递进来.可以是 NULL. .. versionchanged:: 1.23.0 除了 NULL 值的数组外,还接受 NULL data.

这是一个专门用于返回双精度数的双精度数加法的函数示例.

static void
double_add(char **args,
           npy_intp const *dimensions,
           npy_intp const *steps,
           void *extra)
{
    npy_intp i;
    npy_intp is1 = steps[0], is2 = steps[1];
    npy_intp os = steps[2], n = dimensions[0];
    char *i1 = args[0], *i2 = args[1], *op = args[2];
    for (i = 0; i < n; i++) {
        *((double *)op) = *((double *)i1) +
                          *((double *)i2);
        i1 += is1;
        i2 += is2;
        op += os;
     }
}

函数#

PyObject *PyUFunc_FromFuncAndData(PyUFuncGenericFunction *func, void *const *data, const char *types, int ntypes, int nin, int nout, int identity, const char *name, const char *doc, int unused)#

从所需变量创建一个新的广播通用函数.每个 ufunc 都围绕逐元素操作的概念构建.每个 ufunc 对象包含指向实现每个支持类型的基本功能的 1-d 循环的指针.

备注

funcdatatypesnamedoc 参数不会被 PyUFunc_FromFuncAndData 复制.调用者必须确保只要 ufunc 对象存在,这些数组使用的内存就不会被释放.

参数:
  • func – 必须指向一个包含 ntypes PyUFuncGenericFunction 元素的数组.

  • data – 应为 NULL 或指向大小为 ntypes 的数组的指针.该数组可能包含要传递给 func 数组中相应循环函数的任意额外数据,包括 NULL.

  • types – 长度 (nin + nout) * ntypeschar 数组,编码 func 数组中相应函数接受的 numpy.dtype.num`(仅限内置).例如,对于一个有三个 ``ntypes`、两个 nin 和一个 nout 的比较 ufunc,其中第一个函数接受 numpy.int32,第二个接受 numpy.int64,两者都返回 numpy.bool_,``types`` 将是 (char[]) {5, 5, 0, 7, 7, 0},因为 NPY_INT32 是 5,``NPY_INT64`` 是 7,``NPY_BOOL`` 是 0.如果需要,也可以使用位宽名称(例如 NPY_INT32, NPY_COMPLEX128).运行时将使用 类型转换规则 来找到第一个可由提供的输入/输出调用的 func.

  • ntypes – ufunc 实现了多少种不同的数据类型特定函数.

  • nin – 此操作的输入数量.

  • nout – 输出数量

  • identity – 要么 PyUFunc_One ,要么 PyUFunc_Zero ,要么 PyUFunc_MinusOne ,或者 PyUFunc_None .这指定了当一个空数组传递给 ufunc 的 reduce 方法时应该返回什么.特殊值 PyUFunc_IdentityValue 只能与 PyUFunc_FromFuncAndDataAndSignatureAndIdentity 方法一起使用,以允许使用任意 python 对象作为标识.

  • name – 作为以 NULL 结尾的字符串的 ufunc 名称.指定名称为 ‘add’ 或 ‘multiply’ 时,在未指定 dtype 的情况下,对于整数类型的归约操作会启用一种特殊行为.如果输入类型是小于 numpy.int_ 数据类型大小的整数(或布尔)数据类型,它将被内部向上转换为 numpy.int_`(或 `numpy.uint)数据类型.

  • doc – 允许传入一个文档字符串,该字符串将与 ufunc 一起存储.文档字符串不应包含函数名称或调用签名,因为这些将根据对象动态确定,并在访问 ufunc 的 __doc__ 属性时可用.

  • unused – 未使用,仅为向后兼容C-API而存在.

PyObject *PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void *const *data, const char *types, int ntypes, int nin, int nout, int identity, const char *name, const char *doc, int unused, const char *signature)#

这个函数与上面的 PyUFunc_FromFuncAndData 非常相似,但有一个额外的 signature 参数,用于定义一个 广义通用函数 .类似于 ufuncs 围绕元素对元素的操作构建,gufuncs 围绕子数组对子数组的操作构建,:ref:签名 定义了要操作的子数组.

参数:
  • signature – 新 gufunc 的签名.将其设置为 NULL 相当于调用 PyUFunc_FromFuncAndData.会创建字符串的副本,因此可以释放传入的缓冲区.

PyObject *PyUFunc_FromFuncAndDataAndSignatureAndIdentity(PyUFuncGenericFunction *func, void **data, char *types, int ntypes, int nin, int nout, int identity, char *name, char *doc, int unused, char *signature, PyObject *identity_value)#

这个函数与上面的 PyUFunc_FromFuncAndDataAndSignature 非常相似,但有一个额外的 identity_value 参数,用于在 identity 作为 PyUFunc_IdentityValue 传递时定义一个任意的标识.

参数:
  • identity_value – 新 gufunc 的标识.除非 identity 参数是 PyUFunc_IdentityValue,否则必须传递 NULL.将其设置为 NULL 等同于调用 PyUFunc_FromFuncAndDataAndSignature.

int PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, int usertype, PyUFuncGenericFunction function, int *arg_types, void *data)#

此函数允许用户使用已创建的 ufunc 注册一个 1-d 循环,以便在 ufunc 使用任何用户定义的数据类型作为输入参数调用时使用.这是为了使 ufunc 能够与内置数据类型一起工作所必需的.数据类型必须事先在 numpy 系统中注册.循环作为 function 传递.此循环可以接受任意数据,应作为 data 传递.循环所需的数据类型作为 arg_types 传递,该类型必须是指向至少与 ufunc->nargs 一样大的内存的指针.

int PyUFunc_RegisterLoopForDescr(PyUFuncObject *ufunc, PyArray_Descr *userdtype, PyUFuncGenericFunction function, PyArray_Descr **arg_dtypes, void *data)#

此函数的行为类似于上述的 PyUFunc_RegisterLoopForType,但它允许用户使用 PyArray_Descr 对象而不是 dtype 类型编号值来注册 1-d 循环.这允许为结构化数组数据类型和自定义数据类型而不是标量数据类型注册 1-d 循环.

int PyUFunc_ReplaceLoopBySignature(PyUFuncObject *ufunc, PyUFuncGenericFunction newfunc, int *signature, PyUFuncGenericFunction *oldfunc)#

在已经创建的 ufunc 中,用新的 1-d 循环 newfunc 替换与给定 signature 匹配的 1-d 循环.在 oldfunc 中返回旧的 1-d 循环函数.成功时返回 0,失败时返回 -1.此函数仅适用于内置类型(对于用户定义的类型,请使用 PyUFunc_RegisterLoopForType).签名是一个数据类型数组,指示输入后跟 1-d 循环假定的输出.

void PyUFunc_clearfperr()#

清除 IEEE 错误标志.

通用函数#

在每个 ufunc 的核心是一个特定类型的函数集合,这些函数为每种支持的类型定义了基本功能.这些函数必须评估底层函数 \(N\geq1\) 次.可以传递额外的数据,这些数据可以在计算过程中使用.此功能允许一些通用函数用作这些基本的循环函数.通用函数包含所有需要将变量指向正确位置并设置函数调用的代码.通用函数假设实际调用的函数作为额外数据传递进来,并使用正确的值调用它.所有这些函数都适合直接放置在 PyUFuncObject 结构体的 functions 成员中存储的函数数组中.

void PyUFunc_f_f_As_d_d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_d_d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_f_f(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_g_g(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_F_F_As_D_D(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_F_F(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_D_D(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_G_G(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_e_e(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_e_e_As_f_f(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_e_e_As_d_d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#

特定类型的核心 1-d 函数,用于 ufuncs,其中每个计算通过调用一个接受一个输入参数并返回一个输出结果的函数获得.这个函数通过 func 传递.字母对应于支持的数据类型的 dtypechar( e - 半精度, f - 浮点数, d - 双精度, g - 长双精度, F - 复数浮点数, D - 复数双精度, G - 复数长双精度).参数 func 必须支持相同的签名._As_X_X 变体假设 ndarray 的数据类型但将值转换为使用一个接受不同数据类型的底层函数.因此, PyUFunc_f_f_As_d_d 使用数据类型为 NPY_FLOAT 的 ndarray,但调用一个接受双精度并返回双精度的 C 函数.

void PyUFunc_ff_f_As_dd_d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_ff_f(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_dd_d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_gg_g(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_FF_F_As_DD_D(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_DD_D(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_FF_F(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_GG_G(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_ee_e(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_ee_e_As_ff_f(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_ee_e_As_dd_d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#

特定类型的核心 1-d 函数,用于 ufuncs,其中每个计算通过调用一个接受两个输入参数并返回一个输出的函数获得.底层调用的函数作为 func 传递.字母对应于通用函数支持的特定数据类型的 dtypechar.参数 func 必须支持相应的签名.``_As_XX_X`` 变体假设一种数据类型的 ndarrays,但在循环的每次迭代中将值转换为使用接受不同数据类型的底层函数.

void PyUFunc_O_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
void PyUFunc_OO_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#

单输入、单输出和双输入、单输出的核心 1-d 函数用于 NPY_OBJECT 数据类型.这些函数处理引用计数问题,并在错误时提前返回.实际调用的函数是 func,它必须接受具有以下签名的调用:(PyObject*) (PyObject*) 用于 PyUFunc_O_O(PyObject*)(PyObject *, PyObject *) 用于 PyUFunc_OO_O.

void PyUFunc_O_O_method(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#

这个通用的1-d核心函数假设 func 是一个字符串,表示输入对象的方法.对于循环的每次迭代,Python对象从数组中提取,并调用其 func 方法,将结果返回给输出数组.

void PyUFunc_OO_O_method(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#

这个通用的1-d核心函数假设 func 是一个字符串,表示输入对象的方法,该方法接受一个参数.*args* 中的第一个参数是调用其函数的方法,*args* 中的第二个参数是传递给函数的参数.函数的输出存储在 args 的第三个条目中.

void PyUFunc_On_Om(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#

这是由 umath.frompyfunc(function, nin, nout) 创建的动态 ufuncs 使用的 1-d 核心函数.在这种情况下,*func* 是指向一个 PyUFunc_PyFuncData 结构体的指针,该结构体有定义

type PyUFunc_PyFuncData#
typedef struct {
    int nin;
    int nout;
    PyObject *callable;
} PyUFunc_PyFuncData;

在每次循环迭代中,从它们的对象数组中提取 nin 个输入对象并放入一个参数元组,使用输入参数调用 Python callable,并将 nout 个输出放入它们的对象数组中.

导入 API#

PY_UFUNC_UNIQUE_SYMBOL#
NO_IMPORT_UFUNC#
int PyUFunc_ImportUFuncAPI(void)#

确保 UFunc C-API 被导入并可用.如果成功则返回 0,如果无法导入 NumPy 则返回 -1 并设置错误.虽然最好在模块初始化时调用一次,但如果多次调用此函数,它的开销非常小.

在 2.0 版本加入: 此函数主要检查 PyUFunc_API == NULL ,因此如果需要,可以手动回移.

import_ufunc(void)#

这些是用于从扩展模块访问 ufunc C-API 的常量和函数,方式与访问数组 C-API 的方式完全相同.必须始终调用 import_ufunc () 函数(在扩展模块的初始化子例程中).如果您的扩展模块在一个文件中,那么这就是所有需要的.其他两个常量在您的扩展模块使用多个文件时非常有用.在这种情况下,将 PY_UFUNC_UNIQUE_SYMBOL 定义为您的代码独有的名称,然后在不包含模块初始化函数但仍需要访问 UFUNC API 的源文件中,将 PY_UFUNC_UNIQUE_SYMBOL 定义为之前使用的相同名称,并定义 NO_IMPORT_UFUNC.

C-API 实际上是一个函数指针数组.这个数组由 import_ufunc 创建(并通过一个全局变量指向它).全局变量可以是静态定义的,也可以根据 PY_UFUNC_UNIQUE_SYMBOLNO_IMPORT_UFUNC 的状态允许被其他文件看到.