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#

Macros#

NPY_LOOP_BEGIN_THREADS#

在通用函数代码中使用,仅在 loop->obj 不成立(,这不是 OBJECT 数组循环)的情况下释放 Python GIL。需要在变量声明区域使用 NPY_BEGIN_THREADS_DEF

NPY_LOOP_END_THREADS#

在通用函数代码中使用,如果已释放,则重新获取 Python GIL(因为 loop->obj 不成立)。

Types#

type PyUFuncGenericFunction#

实际实现底层(逐个元素)函数的函数指针 \(N\) 次,signature 如下所示

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 数据

以下是 func 的示例,专门用于返回 double 的 double 加法。

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 维循环的指针,用于为各受支持类型实现基本功能。

注意

funcdatatypesnamedoc 参数未通过 PyUFunc_FromFuncAndData 复制。调用方必须确保 ufunc 对象处于活动状态时,这些数组所使用的内存不会释放。

参数:
  • func - 必须指向包含 ntypes PyUFuncGenericFunction 元素的数组。

  • data – 应为 NULL 或指向大小为 ntypes 的数组的指针。此数组可能包含将传递给 func 数组中相应循环函数的任意额外的 data,包括 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_INT32NPY_COMPLEX128)。

    类型转换规则 将在运行时使用,以查找第一个可由所提供的输入/输出调用的 func

  • ntypes – ufunc 已实现的不同数据类型特定函数的数量。

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

  • nout – 输出的数量。

  • identitiyPyUFunc_OnePyUFunc_ZeroPyUFunc_MinusOnePyUFunc_None 中的一个。当向 ufunc 的 reduce 方法传递空数组时,这指定应返回的内容。特殊值 PyUFunc_IdentityValue 只能与 PyUFunc_FromFuncAndDataAndSignatureAndIdentity 方法一同使用,以允许将任意 Python 对象用作 identitiy。

  • 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 以逐子数组操作为基础,签名 定义需要操作的子数组。

参数:
  • 签名 – 新 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_value – 新gufunc的同一性。必须作为NULL传递,除非identity参数为PyUFunc_IdentityValue。将其设置为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 对象而不是数据类型数字值来注册 1-d 循环。这允许为结构化数组数据类型和自定义数据类型而不是标量数据类型注册 1-d 循环。

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

使用新的一维循环 newfunc 替换已创建的 ufunc 中匹配给定签名 signature 的一维循环。将旧的一维循环函数返回到 oldfunc 中。成功返回 0,失败返回 -1。此函数仅适用于内置类型(用户自定义类型请使用 PyUFunc_RegisterLoopForType)。签名是由数据类型数字组成的数组,表示一维循环假定的输入和输出。

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 **参数, npy_intp const *维度, npy_intp const *步长, void *func)#
void PyUFunc_e_e_As_d_d(char **参数, npy_intp const *维度, npy_intp const *步长, 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)#

特定于类型、uFunc 的核心一维函数,其中每个计算由调用采用两个输入参数并返回一个输出的函数来获得。要调用的底层函数作为func传递。这些字母对应于通用函数支持的特定数据类型的 dtypechar。参数 func 必须支持相应的签名。 _As_XX_X 变体假设一个数据类型的一维数组,但是将循环中每次迭代中的值强制转换为采用不同数据类型底层函数。

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**,它必须接受带有针对 PyUFunc_O_O 的签名 (PyObject*) (PyObject*) 或针对 PyUFunc_OO_O 的签名 (PyObject*)(PyObject *, PyObject *) 的调用。

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

此通用一维函数核心函数假定 **func** 是一个字符串,表示输入对象的某个方法。对于循环的每次迭代,都会从数组中提取 Python 对象,并调用其 **func** 方法,并将结果返回输出数组。

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

此通用型一维核心函数假设*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 使用。在此情况下,funcPyUFunc_PyFuncData结构的指针,其定义为:

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

在循环的每次迭代过程中,nin 个输入对象会从其对象数组中提取出来并放置到一个论元元组中,Python 可调用对象 使用输入论元调用,并且将 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 的常量和函数,其方式与访问 array C-API 的方式完全相同。始终必须调用 import_ufunc() 函数(在扩展模块的初始化子例程中)。如果扩展模块在一个文件中,那么就是全部要求。如果你要为扩展模块使用多个文件,其他两个常量就很有用了。在这种情况下,将 PY_UFUNC_UNIQUE_SYMBOL 定义为对你的代码唯一的,然后将不包含模块初始化函数但仍需要访问 UFUNC API 的源文件中的 PY_UFUNC_UNIQUE_SYMBOL 定义为先前使用的相同的名称,还要定义 NO_IMPORT_UFUNC

C-API 实际上是函数指针数组。此数组由 import_ufunc 创建(并由全局变量指向)。全局变量在 static 模式下定义,或允许由其他文件查看,具体取决于 PY_UFUNC_UNIQUE_SYMBOLNO_IMPORT_UFUNC 的状态。