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 不为真时(即,这不是 OBJECT 数组循环)才释放 Python GIL。需要在使用变量声明区域中的 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

    在 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 循环的指针,这些循环为每种支持的类型实现基本功能。

注意

PyUFunc_FromFuncAndData 不会复制 *func*、*data*、*types*、*name* 和 *doc* 参数。调用者必须确保只要 ufunc 对象存在,这些数组使用的内存就不会被释放。

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

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

  • types

    长度为 (nin + nout) * ntypeschar 数组,用于编码 numpy.dtype.num (仅限内置类型),该类型是 func 数组中对应函数所接受的类型。例如,对于一个具有三个 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 – 输出的数量。

  • identityPyUFunc_One, PyUFunc_Zero, PyUFunc_MinusOne, 或 PyUFunc_None 之一。这指定了当空数组传递给 ufunc 的 reduce 方法时应返回的内容。特殊值 PyUFunc_IdentityValue 只能与 PyUFunc_FromFuncAndDataAndSignatureAndIdentity 方法一起使用,以允许将任意 Python 对象用作 identity。

  • name – ufunc 的名称,以 NULL 结尾的字符串形式表示。指定名称为 “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 参数,用于定义广义通用函数。与 ufunc 围绕逐元素操作构建类似,gufunc 围绕逐子数组操作构建,而signature定义要操作的子数组。

参数:
  • 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 传递时,为 ufunc 定义任意 identity。

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

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

此函数允许用户向已创建的 ufunc 注册一个一维循环,以便在调用 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 类型数值来注册一维循环。这允许为结构化数组数据类型和自定义数据类型注册一维循环,而不是标量数据类型。

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

用新的 1-d 循环 newfunc 替换已创建的 ufunc 中与给定签名匹配的 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)#

用于 ufunc 的类型特定的核心一维函数,其中每个计算都通过调用一个带有一个输入参数并返回一个输出的函数获得。此函数在 func 中传递。字母对应于支持的数据类型的 dtypechar(e - half, f - float, d - double, g - long double, F - cfloat, D - cdouble, G - clongdouble)。参数 func 必须支持相同的签名。_As_X_X 变体假定 ndarray 具有一种数据类型,但转换值以使用采用不同数据类型的底层函数。因此,PyUFunc_f_f_As_d_d 使用数据类型为 NPY_FLOAT 的 ndarray,但调用一个接受 double 并返回 double 的 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)#

ufunc 的特定类型核心一维函数,其中每次计算都通过调用一个接受两个输入参数并返回一个输出的函数获得。要调用的底层函数作为 func 传入。字母对应于通用函数支持的特定数据类型的 dtypechar。参数 func 必须支持相应的签名。_As_XX_X 变体假定为一种数据类型的 ndarray,但在循环的每次迭代中转换这些值,以便使用接受不同数据类型的底层函数。

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)#

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 维核心函数假定 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) 创建的动态 ufunc 使用的 1 维核心函数。在这种情况下,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)#

这些常量和函数用于以与访问数组 C-API 完全相同的方式从扩展模块访问 ufunc 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 的状态。