ufunc API#
常量#
UFUNC_{THING}_{ERR}
宏#
-
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; } }
-
void loopfunc(char **args, npy_intp const *dimensions, npy_intp const *steps, void *data)#
函数#
-
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 循环的指针。
注意
*func*、*data*、*types*、*name* 和 *doc* 参数不会被
PyUFunc_FromFuncAndData
复制。调用者必须确保只要 ufunc 对象存在,这些数组使用的内存就不会被释放。- 参数:
func – 必须指向一个包含 *ntypes* 个
PyUFuncGenericFunction
元素的数组。data – 应该为
NULL
或指向一个大小为 *ntypes* 的数组的指针。此数组可以包含任意额外数据,这些数据将被传递给 func 数组中相应的循环函数,包括NULL
。types –
长度为
(nin + nout) * ntypes
的char
数组,编码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 – 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* 参数,用于定义广义通用函数。与 ufuncs 围绕逐元素操作构建类似,gufuncs 围绕逐子数组操作构建,其中签名定义了要操作的子数组。
- 参数:
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_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* 中与给定 *signature* 匹配的 1-d 循环替换为新的 1-d 循环 newfunc。将旧的 1-d 循环函数返回到 *oldfunc* 中。成功时返回 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_F_F_As_D_D(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 的特定类型核心 1-d 函数,其中每个计算通过调用一个接受一个输入参数并返回一个输出的函数来获得。此函数通过 `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_As_DD_D(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 的特定类型核心 1-d 函数,其中每个计算通过调用一个接受两个输入参数并返回一个输出的函数来获得。要调用的底层函数作为 *func* 传入。这些字母对应于通用函数支持的特定数据类型的 dtypechar。参数 `func` 必须支持相应的签名。
_As_XX_X
变体假定 ndarray 是一种数据类型,但会在循环的每次迭代中将值转换为使用接受不同数据类型的底层函数。
-
void PyUFunc_OO_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)#
NPY_OBJECT
数据类型的一输入、一输出和两输入、一输出核心 1-d 函数。这些函数处理引用计数问题并在出错时提前返回。要调用的实际函数是 *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)#
此通用 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 个输出放入其对象数组中。
-
type PyUFunc_PyFuncData#
导入 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_SYMBOL
和NO_IMPORT_UFUNC
的状态。