NumPy 源码解析(七十八)
.\numpy\numpy\_core\src\umath\ufunc_object.h
// 声明一个宏,用于条件编译,防止重复包含头文件 _NPY_UMATH_UFUNC_OBJECT_H_
// 声明一个不导出的函数,返回一个指向 ufunc 名称的 C 字符串
NPY_NO_EXPORT const char*
ufunc_get_name_cstr(PyUFuncObject *ufunc);
// 声明一个不导出的函数,返回一个 ufunc 的默认标识对象,同时指示是否可重新排序
NPY_NO_EXPORT PyObject *
PyUFunc_GetDefaultIdentity(PyUFuncObject *ufunc, npy_bool *reorderable);
这段代码是一个 C 语言头文件,主要定义了一些用于处理 NumPy 的 ufunc 对象的函数声明。
.\numpy\numpy\_core\src\umath\ufunc_type_resolution.c
/*
* NOTE: The type resolution defined in this file is considered legacy.
*
* The new mechanism separates type resolution and promotion into two
* distinct steps, as per NEP 43.
* Further, the functions in this file rely on the operands rather than
* only the DTypes/descriptors. They are still called and at this point
* vital (NumPy ~1.21), but should hopefully become largely irrelevant very
* quickly.
*
* At that point, this file should be deletable in its entirety.
*
*
* This file implements type resolution for NumPy element-wise ufuncs.
* This mechanism is still backwards-compatible with the pre-existing
* legacy mechanism, so performs much slower than is necessary.
*
* Written by Mark Wiebe (mwwiebe@gmail.com)
* Copyright (c) 2011 by Enthought, Inc.
*
* See LICENSE.txt for the license.
*/
// printif debug tracing
/**
* Converts the NumPy casting enum `casting` to a Python string object.
* Returns a Python object representing the casting type.
*/
static PyObject *
npy_casting_to_py_object(NPY_CASTING casting)
{
switch (casting) {
case NPY_NO_CASTING:
return PyUnicode_FromString("no");
case NPY_EQUIV_CASTING:
return PyUnicode_FromString("equiv");
case NPY_SAFE_CASTING:
return PyUnicode_FromString("safe");
case NPY_SAME_KIND_CASTING:
return PyUnicode_FromString("same_kind");
case NPY_UNSAFE_CASTING:
return PyUnicode_FromString("unsafe");
default:
return PyLong_FromLong(casting);
}
}
/**
* Helper function to raise a binary type resolution error for ufuncs.
* This function raises an exception with detailed information on ufunc,
* operands[0], and operands[1].
* Returns -1 to indicate that an exception was raised.
*/
static int
raise_binary_type_reso_error(PyUFuncObject *ufunc, PyArrayObject **operands) {
PyObject *exc_value;
/* Produce an error object with ufunc, operand[0], and operand[1] descriptors */
exc_value = Py_BuildValue(
"O(OO)", ufunc,
(PyObject *)PyArray_DESCR(operands[0]),
(PyObject *)PyArray_DESCR(operands[1])
);
if (exc_value == NULL){
return -1;
}
PyErr_SetObject(
npy_static_pydata._UFuncBinaryResolutionError, exc_value);
Py_DECREF(exc_value);
return -1;
}
/**
* Helper function to raise UFuncNoLoopError exception for ufuncs.
* This function raises an exception with detailed information on ufunc and dtypes.
* Returns -1 to indicate that an exception was raised.
*/
NPY_NO_EXPORT int
raise_no_loop_found_error(
PyUFuncObject *ufunc, PyObject **dtypes)
{
PyObject *dtypes_tup = PyArray_TupleFromItems(ufunc->nargs, dtypes, 1);
if (dtypes_tup == NULL) {
return -1;
}
/* 创建一个错误对象 */
PyObject *exc_value = PyTuple_Pack(2, ufunc, dtypes_tup);
// 使用 PyTuple_Pack 函数创建一个包含 ufunc 和 dtypes_tup 的元组作为错误对象
Py_DECREF(dtypes_tup); // 减少 dtypes_tup 的引用计数
// 检查错误对象是否创建成功
if (exc_value == NULL) {
return -1; // 如果创建失败,返回 -1 表示错误
}
// 设置异常对象为 _UFuncNoLoopError,并传入之前创建的错误对象
PyErr_SetObject(npy_static_pydata._UFuncNoLoopError, exc_value);
Py_DECREF(exc_value); // 减少错误对象的引用计数
return -1; // 返回 -1 表示函数执行失败
/*
* 静态函数:raise_casting_error
* 抛出类型转换错误异常,用于输入或输出类型转换失败时调用
* 返回:-1 表示异常已经抛出
*/
static int
raise_casting_error(
PyObject *exc_type,
PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArray_Descr *from,
PyArray_Descr *to,
npy_intp i)
{
PyObject *exc_value;
PyObject *casting_value;
// 将 NumPy 的类型转换枚举值转换为 Python 对象
casting_value = npy_casting_to_py_object(casting);
if (casting_value == NULL) {
return -1;
}
// 构建异常对象,包含 ufunc 对象、转换方式、源类型、目标类型和索引
exc_value = Py_BuildValue(
"ONOOi",
ufunc,
casting_value,
(PyObject *)from,
(PyObject *)to,
i
);
if (exc_value == NULL){
return -1;
}
// 设置异常类型和值
PyErr_SetObject(exc_type, exc_value);
Py_DECREF(exc_value);
return -1;
}
/*
* 静态函数:raise_input_casting_error
* 抛出输入类型转换错误异常,始终返回 -1 表示异常已抛出
*/
static int
raise_input_casting_error(
PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArray_Descr *from,
PyArray_Descr *to,
npy_intp i)
{
// 调用 raise_casting_error 函数,抛出输入类型转换错误异常
return raise_casting_error(npy_static_pydata._UFuncInputCastingError,
ufunc, casting, from, to, i);
}
/*
* 静态函数:raise_output_casting_error
* 抛出输出类型转换错误异常,始终返回 -1 表示异常已抛出
*/
static int
raise_output_casting_error(
PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArray_Descr *from,
PyArray_Descr *to,
npy_intp i)
{
// 调用 raise_casting_error 函数,抛出输出类型转换错误异常
return raise_casting_error(npy_static_pydata._UFuncOutputCastingError,
ufunc, casting, from, to, i);
}
/*
* UFUNC_API
*
* 函数:PyUFunc_ValidateCasting
* 验证输入和输出操作数能否按指定的转换方式转换为指定的数据类型
* 返回:成功返回 0,验证失败返回 -1 并抛出异常
*/
NPY_NO_EXPORT int
PyUFunc_ValidateCasting(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyArray_Descr *const *dtypes)
{
int i, nin = ufunc->nin, nop = nin + ufunc->nout;
// 遍历所有操作数(输入和输出)
for (i = 0; i < nop; ++i) {
if (i < nin) {
// 对于输入操作数,验证其能否按指定的转换方式转换为指定数据类型
if (!PyArray_CanCastArrayTo(operands[i], dtypes[i], casting)) {
// 如果无法转换,抛出输入类型转换错误异常
return raise_input_casting_error(
ufunc, casting, PyArray_DESCR(operands[i]), dtypes[i], i);
}
} else if (operands[i] != NULL) {
// 对于输出操作数,验证指定数据类型能否按指定的转换方式转换为其类型
if (!PyArray_CanCastTypeTo(dtypes[i],
PyArray_DESCR(operands[i]), casting)) {
// 如果无法转换,抛出输出类型转换错误异常
return raise_output_casting_error(
ufunc, casting, dtypes[i], PyArray_DESCR(operands[i]), i);
}
}
}
return 0;
}
/*
* 函数:PyUFunc_ValidateOutCasting
* 验证输出操作数能否按指定的转换方式转换为指定的数据类型
* 返回:成功返回 0,验证失败返回 -1 并抛出异常
*/
NPY_NO_EXPORT int
PyUFunc_ValidateOutCasting(PyUFuncObject *ufunc,
NPY_CASTING casting, PyArrayObject **operands, PyArray_Descr **dtypes)
{
int i, nin = ufunc->nin, nop = nin + ufunc->nout;
// 遍历所有输出操作数
for (i = 0; i < nop; ++i) {
if (operands[i] != NULL) {
// 验证输出操作数能否按指定的转换方式转换为指定数据类型
if (!PyArray_CanCastTypeTo(dtypes[i],
PyArray_DESCR(operands[i]), casting)) {
// 如果无法转换,抛出输出类型转换错误异常
return raise_output_casting_error(
ufunc, casting, dtypes[i], PyArray_DESCR(operands[i]), i);
}
}
}
return 0;
}
for (i = nin; i < nop; ++i) {
if (operands[i] == NULL) {
continue;
}
if (!PyArray_CanCastTypeTo(dtypes[i],
PyArray_DESCR(operands[i]), casting)) {
return raise_output_casting_error(
ufunc, casting, dtypes[i], PyArray_DESCR(operands[i]), i);
}
}
return 0;
/*UFUNC_API
*
* This function applies the default type resolution rules
* for the provided ufunc.
*
* Returns 0 on success, -1 on error.
*/
NPY_NO_EXPORT int
PyUFunc_DefaultTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int i, nop = ufunc->nin + ufunc->nout;
int retval = 0, any_object = 0;
NPY_CASTING input_casting;
// 遍历操作数,检查是否有对象数组
for (i = 0; i < nop; ++i) {
if (operands[i] != NULL &&
PyTypeNum_ISOBJECT(PyArray_DESCR(operands[i])->type_num)) {
any_object = 1;
break;
}
}
/*
* Decide the casting rules for inputs and outputs. We want
* NPY_SAFE_CASTING or stricter, so that the loop selection code
* doesn't choose an integer loop for float inputs, or a float32
* loop for float64 inputs.
*/
// 根据传入的 casting 参数确定输入和输出的转换规则
input_casting = (casting > NPY_SAFE_CASTING) ? NPY_SAFE_CASTING : casting;
if (type_tup == NULL) {
/* Find the best ufunc inner loop, and fill in the dtypes */
// 如果 type_tup 为 NULL,则使用线性搜索来确定最佳的内部循环并填充 dtypes
retval = linear_search_type_resolver(ufunc, operands,
input_casting, casting, any_object,
out_dtypes);
} else {
/* Find the specified ufunc inner loop, and fill in the dtypes */
// 如果指定了 type_tup,则使用类型元组解析器来确定指定的内部循环并填充 dtypes
retval = type_tuple_type_resolver(ufunc, type_tup,
operands, input_casting, casting, any_object, out_dtypes);
}
return retval;
}
/*
* This function applies special type resolution rules for the case
* where all the functions have the pattern XX->bool, using
* PyArray_ResultType instead of a linear search to get the best
* loop.
*
* Returns 0 on success, -1 on error.
*/
NPY_NO_EXPORT int
PyUFunc_SimpleBinaryComparisonTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int i, type_num1, type_num2;
const char *ufunc_name = ufunc_get_name_cstr(ufunc);
// 如果输入不符合二进制比较类型解析的条件,抛出运行时错误
if (ufunc->nin != 2 || ufunc->nout != 1) {
PyErr_Format(PyExc_RuntimeError, "ufunc %s is configured "
"to use binary comparison type resolution but has "
"the wrong number of inputs or outputs",
ufunc_name);
return -1;
}
/*
* Use the default type resolution if there's a custom data type
* or object arrays.
*/
// 获取操作数的类型编号
type_num1 = PyArray_DESCR(operands[0])->type_num;
type_num2 = PyArray_DESCR(operands[1])->type_num;
if (type_num1 >= NPY_NTYPES_LEGACY || type_num2 >= NPY_NTYPES_LEGACY ||
type_num1 == NPY_OBJECT || type_num2 == NPY_OBJECT) {
// 如果操作数的类型编号超过了旧版 NumPy 类型的数量,或者其中有任何一个是对象类型,使用默认的类型解析器处理
return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
}
if (type_tup == NULL) {
// 如果类型元组为空
if (PyArray_ISDATETIME(operands[0])
&& PyArray_ISDATETIME(operands[1])
&& type_num1 != type_num2) {
/*
* 拒绝混合的日期时间和时间增量,这总是会失败,因为类型转换会失败(除非使用 `casting="unsafe"`)。
* 这是必需的,以确保 `==` 和 `!=` 能够正确检测并返回结果数组的 False/True。
*/
return raise_binary_type_reso_error(ufunc, operands);
}
/*
* 这个检查是为了避免可能出现的 FutureWarning,ResultType 在数值->字符串提升时会给出警告。
* (我们从未支持灵活的 dtype。)
*/
else if (!PyArray_ISFLEXIBLE(operands[0]) &&
!PyArray_ISFLEXIBLE(operands[1])) {
// 如果操作数都不是灵活的 dtype
out_dtypes[0] = PyArray_ResultType(2, operands, 0, NULL);
if (out_dtypes[0] == NULL) {
// 如果获取结果类型失败,返回 -1
return -1;
}
if (PyArray_ISINTEGER(operands[0])
&& PyArray_ISINTEGER(operands[1])
&& !PyDataType_ISINTEGER(out_dtypes[0])) {
/*
* NumPy 的提升允许无符号整数和有符号整数相加得到浮点数,避免这种情况
* (输入必须是有符号和无符号混合的情况)
*/
if (PyArray_ISSIGNED(operands[0])) {
// 如果第一个操作数是有符号整数
Py_SETREF(out_dtypes[0], PyArray_DescrFromType(NPY_LONGLONG));
out_dtypes[1] = PyArray_DescrFromType(NPY_ULONGLONG);
Py_INCREF(out_dtypes[1]);
}
else {
// 如果第二个操作数是有符号整数
Py_SETREF(out_dtypes[0], PyArray_DescrFromType(NPY_ULONGLONG));
out_dtypes[1] = PyArray_DescrFromType(NPY_LONGLONG);
Py_INCREF(out_dtypes[1]);
}
}
else {
// 否则,输出类型相同
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]);
}
}
else {
// 否则,不做任何处理,继续使用操作数的描述符
/* Not doing anything will lead to a loop no found error. */
out_dtypes[0] = PyArray_DESCR(operands[0]);
Py_INCREF(out_dtypes[0]);
out_dtypes[1] = PyArray_DESCR(operands[1]);
Py_INCREF(out_dtypes[1]);
}
}
else {
// 如果类型元组不为空,通常是失败的情况,让默认版本处理
/* Usually a failure, but let the default version handle it */
return PyUFunc_DefaultTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
}
/* 输出类型始终是布尔类型(内置类型不会失败) */
out_dtypes[2] = PyArray_DescrFromType(NPY_BOOL);
/* 检查根据转换规则进行验证 */
if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
/* 如果验证失败,释放输出数据类型对象的引用并置空 */
for (i = 0; i < 3; ++i) {
Py_DECREF(out_dtypes[i]);
out_dtypes[i] = NULL;
}
// 返回错误标志
return -1;
}
// 返回成功标志
return 0;
/*
* PyUFunc_NegativeTypeResolver函数用于解析一元负号操作的数据类型。
* 它调用PyUFunc_SimpleUniformOperationTypeResolver函数来进行统一操作类型的解析。
* 参数解释:
* ufunc: 指向PyUFuncObject结构的指针,表示当前正在处理的ufunc对象。
* casting: 表示操作的转换级别。
* operands: 指向PyArrayObject指针数组的指针,表示操作数数组。
* type_tup: 表示一个Python元组对象,包含用户指定的类型信息。
* out_dtypes: 指向PyArray_Descr指针数组的指针,用于存储输出的数据类型。
*/
NPY_NO_EXPORT int
PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int ret;
// 调用PyUFunc_SimpleUniformOperationTypeResolver进行类型解析
ret = PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
if (ret < 0) {
return ret;
}
/* The type resolver would have upcast already */
// 如果输出的第一个数据类型是布尔类型,抛出类型错误异常
if (out_dtypes[0]->type_num == NPY_BOOL) {
PyErr_Format(PyExc_TypeError,
"The numpy boolean negative, the `-` operator, is not supported, "
"use the `~` operator or the logical_not function instead.");
return -1;
}
return ret;
}
/*
* PyUFunc_OnesLikeTypeResolver函数提供了对ones_like函数的类型解析。
* 当ones_like函数作为ufunc使用时,它总是强制使用UNSAFE转换级别进行类型解析。
* 参数解释:
* ufunc: 指向PyUFuncObject结构的指针,表示当前正在处理的ufunc对象。
* casting: 表示操作的转换级别(此处未使用)。
* operands: 指向PyArrayObject指针数组的指针,表示操作数数组。
* type_tup: 表示一个Python元组对象,包含用户指定的类型信息。
* out_dtypes: 指向PyArray_Descr指针数组的指针,用于存储输出的数据类型。
*/
NPY_NO_EXPORT int
PyUFunc_OnesLikeTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING NPY_UNUSED(casting),
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
// 调用PyUFunc_SimpleUniformOperationTypeResolver进行类型解析,强制使用UNSAFE转换级别
return PyUFunc_SimpleUniformOperationTypeResolver(ufunc,
NPY_UNSAFE_CASTING,
operands, type_tup, out_dtypes);
}
/*
* PyUFunc_SimpleUniformOperationTypeResolver函数用于处理所有输入类型相同的ufunc操作。
* 它利用PyArray_ResultType而不是线性搜索来获取最佳的循环。
* 返回0表示成功,返回-1表示失败。
* 参数解释:
* ufunc: 指向PyUFuncObject结构的指针,表示当前正在处理的ufunc对象。
* casting: 表示操作的转换级别。
* operands: 指向PyArrayObject指针数组的指针,表示操作数数组。
* type_tup: 表示一个Python元组对象,包含用户指定的类型信息。
* out_dtypes: 指向PyArray_Descr指针数组的指针,用于存储输出的数据类型。
*/
NPY_NO_EXPORT int
PyUFunc_SimpleUniformOperationTypeResolver(
PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
// 获取当前ufunc的名称
const char *ufunc_name = ufunc_get_name_cstr(ufunc);
// 检查ufunc的输入数是否小于1,如果是则抛出运行时错误
if (ufunc->nin < 1) {
PyErr_Format(PyExc_RuntimeError, "ufunc %s is configured "
"to use uniform operation type resolution but has "
"no inputs",
ufunc_name);
return -1;
}
int nop = ufunc->nin + ufunc->nout;
/*
* 判断是否存在自定义数据类型或对象数组
*/
bool has_custom_or_object = false;
for (int iop = 0; iop < ufunc->nin; iop++) {
int type_num = PyArray_DESCR(operands[iop])->type_num;
// 如果类型号大于等于NPY_NTYPES_LEGACY或者等于NPY_OBJECT,表示存在自定义数据类型或对象数组
if (type_num >= NPY_NTYPES_LEGACY || type_num == NPY_OBJECT) {
has_custom_or_object = true;
break;
}
}
// 如果存在自定义数据类型或对象数组,则调用默认的类型解析器
if (has_custom_or_object) {
return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
}
if (type_tup == NULL) {
if (ufunc->nin == 1){
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[0]));
}
else {
int iop;
npy_bool has_flexible = 0;
npy_bool has_object = 0;
for (iop = 0; iop < ufunc->nin; iop++) {
if (PyArray_ISOBJECT(operands[iop])) {
has_object = 1;
}
if (PyArray_ISFLEXIBLE(operands[iop])) {
has_flexible = 1;
}
}
if (NPY_UNLIKELY(has_flexible && !has_object)) {
/*
* NumPy 1.20 弃用提醒,2020-12。
* 此检查是为了避免 ResultType 在数字->字符串提升时产生的 FutureWarning。
* (我们从不支持这里的灵活 dtype。)
*/
for (iop = 0; iop < ufunc->nin; iop++) {
out_dtypes[iop] = PyArray_DESCR(operands[iop]);
Py_INCREF(out_dtypes[iop]);
}
raise_no_loop_found_error(ufunc, (PyObject **)out_dtypes);
for (iop = 0; iop < ufunc->nin; iop++) {
Py_DECREF(out_dtypes[iop]);
out_dtypes[iop] = NULL;
}
return -1;
}
out_dtypes[0] = PyArray_ResultType(ufunc->nin, operands, 0, NULL);
}
if (out_dtypes[0] == NULL) {
return -1;
}
}
else {
/*
* 这是一个快速路径,因为所有描述符都将是相同的,主要是当只传递了单个描述符时
* (这将设置元组中的输出描述符),就没有必要检查所有循环。
* 注意,这也允许(None, None, float64)解析为(float64, float64, float64),
* 即使输入不匹配,修复签名的输出部分可以修复所有这些情况。
* 这是支持 `nextafter(1., inf, dtype=float32)` 所必需的,这里很“清楚”我们想要将 1. 和 inf 转换为 float32。
*/
PyArray_Descr *descr = NULL;
if (PyTuple_CheckExact(type_tup) &&
PyTuple_GET_SIZE(type_tup) == nop) {
for (int i = 0; i < nop; i++) {
PyObject *item = PyTuple_GET_ITEM(type_tup, i);
if (item == Py_None) {
if (i < ufunc->nin) {
continue;
}
/* 所有输出必须被设置(这可能会放宽) */
descr = NULL;
break;
}
if (!PyArray_DescrCheck(item)) {
/* 推迟到默认解析器(将在那里引发错误) */
descr = NULL;
break;
}
if (descr != NULL && descr != (PyArray_Descr *)item) {
/* 描述符不匹配:尝试使用默认值(可能是错误) */
descr = NULL;
break;
}
descr = (PyArray_Descr *)item;
}
}
if (descr == NULL) {
/* 在所有坏/不太可能的情况下,使用默认类型解析器: */
return PyUFunc_DefaultTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
}
else if (descr->type_num == PyArray_DESCR(operands[0])->type_num) {
/* 如果匹配,则优先使用输入描述符(保留元数据) */
descr = PyArray_DESCR(operands[0]);
}
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(descr);
}
/* 所有类型相同 - 将第一个复制到其余部分 */
for (int iop = 1; iop < nop; iop++) {
out_dtypes[iop] = out_dtypes[0];
Py_INCREF(out_dtypes[iop]);
}
/* 根据类型转换规则进行检查 */
if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
for (int iop = 0; iop < nop; iop++) {
Py_DECREF(out_dtypes[iop]);
out_dtypes[iop] = NULL;
}
return -1;
}
return 0;
/*
* This function applies special type resolution rules for the absolute
* ufunc. This ufunc converts complex -> float, so isn't covered
* by the simple unary type resolution.
*
* Returns 0 on success, -1 on error.
*/
NPY_NO_EXPORT int
PyUFunc_AbsoluteTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
/* Use the default for complex types, to find the loop producing float */
// 如果操作数的数据类型是复数类型,使用默认的类型解析器来确定生成浮点数的循环
if (PyTypeNum_ISCOMPLEX(PyArray_DESCR(operands[0])->type_num)) {
return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
}
else {
// 对于非复数类型,使用简单统一操作类型解析器
return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
}
}
/*
* This function applies special type resolution rules for the isnat
* ufunc. This ufunc converts datetime/timedelta -> bool, and is not covered
* by the simple unary type resolution.
*
* Returns 0 on success, -1 on error.
*/
NPY_NO_EXPORT int
PyUFunc_IsNaTTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
// 如果操作数的数据类型不是日期时间类型,设置类型错误并返回 -1
if (!PyTypeNum_ISDATETIME(PyArray_DESCR(operands[0])->type_num)) {
PyErr_SetString(PyExc_TypeError,
"ufunc 'isnat' is only defined for np.datetime64 and np.timedelta64.");
return -1;
}
// 为输出的数据类型设置日期时间的规范化描述符和布尔型描述符
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(PyArray_DESCR(operands[0]));
out_dtypes[1] = PyArray_DescrFromType(NPY_BOOL);
return 0;
}
NPY_NO_EXPORT int
PyUFunc_IsFiniteTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
// 如果操作数的数据类型不是日期时间类型,使用默认的类型解析器
if (!PyTypeNum_ISDATETIME(PyArray_DESCR(operands[0])->type_num)) {
return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
}
// 为输出的数据类型设置日期时间的规范化描述符和布尔型描述符
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(PyArray_DESCR(operands[0]));
out_dtypes[1] = PyArray_DescrFromType(NPY_BOOL);
return 0;
}
/*
* Creates a new NPY_TIMEDELTA dtype, copying the datetime metadata
* from the given dtype.
*
* NOTE: This function is copied from datetime.c in multiarray,
* because umath and multiarray are not linked together.
*/
static PyArray_Descr *
timedelta_dtype_with_copied_meta(PyArray_Descr *dtype)
{
PyArray_Descr *ret;
PyArray_DatetimeMetaData *dst, *src;
PyArray_DatetimeDTypeMetaData *dst_dtmd, *src_dtmd;
// 创建一个新的时间增量类型描述符,并从给定的类型描述符复制日期时间元数据
ret = PyArray_DescrNewFromType(NPY_TIMEDELTA);
if (ret == NULL) {
return NULL;
}
// 将输入数据类型的日期时间元数据指针转换为旧式描述符的指针,然后获取其C风格的元数据结构体
src_dtmd = (PyArray_DatetimeDTypeMetaData *)((_PyArray_LegacyDescr *)dtype)->c_metadata;
// 将返回数据类型的日期时间元数据指针转换为旧式描述符的指针,然后获取其C风格的元数据结构体
dst_dtmd = (PyArray_DatetimeDTypeMetaData *)((_PyArray_LegacyDescr *)ret)->c_metadata;
// 获取源数据类型的日期时间元数据的元数据结构体指针
src = &(src_dtmd->meta);
// 获取目标数据类型的日期时间元数据的元数据结构体指针
dst = &(dst_dtmd->meta);
// 将源元数据结构体的内容复制到目标元数据结构体
*dst = *src;
// 返回处理后的数据类型描述符
return ret;
/*
* This function applies the type resolution rules for addition.
* In particular, there are special cases for string and unicode types,
* as well as specific cases involving datetime types:
* m8[<A>] + m8[<B>] => m8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)]
* m8[<A>] + int => m8[<A>] + m8[<A>]
* int + m8[<A>] => m8[<A>] + m8[<A>]
* M8[<A>] + int => M8[<A>] + m8[<A>]
* int + M8[<A>] => m8[<A>] + M8[<A>]
* M8[<A>] + m8[<B>] => M8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)]
* m8[<A>] + M8[<B>] => m8[gcd(<A>,<B>)] + M8[gcd(<A>,<B>)]
* TODO: Non-linear time unit cases require highly specialized loops
* M8[<A>] + m8[Y|M|B]
* m8[Y|M|B] + M8[<A>]
*/
NPY_NO_EXPORT int
PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int type_num1, type_num2;
int i;
// Get the type number of the first and second operands
type_num1 = PyArray_DESCR(operands[0])->type_num;
type_num2 = PyArray_DESCR(operands[1])->type_num;
/* Use the default resolver when neither operand involves datetime,
* timedelta, string, or unicode types */
if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)
&& !(PyTypeNum_ISSTRING(type_num1) && PyTypeNum_ISSTRING(type_num2))) {
return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
}
// Handle cases where both operands are of string or unicode types
if ((type_num1 == NPY_STRING && type_num2 == NPY_STRING)
|| (type_num1 == NPY_UNICODE && type_num2 == NPY_UNICODE)) {
// Set the output dtypes to match the input dtypes
// This is required to ensure compatibility with the loop implementation
out_dtypes[0] = PyArray_DescrFromType(type_num1);
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]); // Increment reference count for the dtype
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]); // Increment reference count for the dtype
// The function does not return yet; it proceeds with more specific cases
// involving datetime and timedelta types
} else if (type_num1 == NPY_TIMEDELTA) {
/* 若 type_num1 是 NPY_TIMEDELTA 类型 */
/* m8[<A>] + m8[<B>] => m8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)] */
if (type_num2 == NPY_TIMEDELTA) {
// 若 type_num2 也是 NPY_TIMEDELTA 类型,则需要计算类型的提升
out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
PyArray_DESCR(operands[1]));
if (out_dtypes[0] == NULL) {
return -1; // 处理错误情况
}
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]);
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
}
/* m8[<A>] + M8[<B>] => m8[gcd(<A>,<B>)] + M8[gcd(<A>,<B>)] */
else if (type_num2 == NPY_DATETIME) {
// 若 type_num2 是 NPY_DATETIME 类型,则需要特殊处理
out_dtypes[1] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
PyArray_DESCR(operands[1]));
if (out_dtypes[1] == NULL) {
return -1; // 处理错误情况
}
/* 创建一个新的 NPY_TIMEDELTA,并复制 datetime 的元数据 */
out_dtypes[0] = timedelta_dtype_with_copied_meta(out_dtypes[1]);
if (out_dtypes[0] == NULL) {
Py_DECREF(out_dtypes[1]);
out_dtypes[1] = NULL;
return -1; // 处理错误情况
}
out_dtypes[2] = out_dtypes[1];
Py_INCREF(out_dtypes[2]);
}
/* m8[<A>] + int => m8[<A>] + m8[<A>] */
else if (PyTypeNum_ISINTEGER(type_num2) ||
PyTypeNum_ISBOOL(type_num2)) {
// 若 type_num2 是整数或布尔型,则需要确保类型为 NPY_TIMEDELTA
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[0]));
if (out_dtypes[0] == NULL) {
return -1; // 处理错误情况
}
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]);
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
type_num2 = NPY_TIMEDELTA; // 强制将 type_num2 设为 NPY_TIMEDELTA
}
else {
return raise_binary_type_reso_error(ufunc, operands);
// 若不满足上述情况,则抛出类型解析错误
}
}
else if (type_num1 == NPY_DATETIME) {
/* 如果 type_num1 是 NPY_DATETIME 类型 */
/* M8[<A>] + m8[<B>] => M8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)] */
/* 如果 type_num2 也是 NPY_TIMEDELTA 类型,则处理如下 */
if (type_num2 == NPY_TIMEDELTA) {
/* 推断操作数的输出类型 */
out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
PyArray_DESCR(operands[1]));
if (out_dtypes[0] == NULL) {
return -1;
}
/* 创建一个新的 NPY_TIMEDELTA 类型,并复制 datetime 的元数据 */
out_dtypes[1] = timedelta_dtype_with_copied_meta(out_dtypes[0]);
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1;
}
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
}
/* 如果 type_num2 是整数类型或布尔类型 */
else if (PyTypeNum_ISINTEGER(type_num2) ||
PyTypeNum_ISBOOL(type_num2)) {
/* 确保第一个操作数的规范类型 */
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[0]));
if (out_dtypes[0] == NULL) {
return -1;
}
/* 创建一个新的 NPY_TIMEDELTA 类型,并复制 type1 的元数据 */
out_dtypes[1] = timedelta_dtype_with_copied_meta(
PyArray_DESCR(operands[0]));
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1;
}
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
/* 将 type_num2 设为 NPY_TIMEDELTA */
type_num2 = NPY_TIMEDELTA;
}
else {
/* 如果 type_num2 不是预期的类型,则触发二进制类型解析错误 */
return raise_binary_type_reso_error(ufunc, operands);
}
}
else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) {
/* 如果 type_num1 是整数或布尔类型 */
/* int + m8[<A>] => m8[<A>] + m8[<A>] */
/* 如果 type_num2 是 NPY_TIMEDELTA */
if (type_num2 == NPY_TIMEDELTA) {
/* 获取操作数 operands[1] 的描述符,并确保它是规范的数据类型 */
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[1]));
/* 如果无法确保规范数据类型,则返回错误 */
if (out_dtypes[0] == NULL) {
return -1;
}
/* 复制数据类型到其他两个输出数据类型 */
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]);
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
/* 将 type_num1 设置为 NPY_TIMEDELTA */
type_num1 = NPY_TIMEDELTA;
}
/* 如果 type_num2 是 NPY_DATETIME */
else if (type_num2 == NPY_DATETIME) {
/* 创建一个带有复制元数据的新 NPY_TIMEDELTA 数据类型 */
out_dtypes[0] = timedelta_dtype_with_copied_meta(
PyArray_DESCR(operands[1]));
/* 如果创建失败,则返回错误 */
if (out_dtypes[0] == NULL) {
return -1;
}
/* 确保 operands[1] 的描述符是规范的数据类型 */
out_dtypes[1] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[1]));
/* 如果无法确保规范数据类型,则释放已分配的内存并返回错误 */
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1;
}
/* 复制数据类型到第三个输出数据类型 */
out_dtypes[2] = out_dtypes[1];
Py_INCREF(out_dtypes[2]);
/* 将 type_num1 设置为 NPY_TIMEDELTA */
type_num1 = NPY_TIMEDELTA;
}
/* 如果 type_num2 不是 NPY_TIMEDELTA 或 NPY_DATETIME,则返回二元类型解析错误 */
else {
return raise_binary_type_reso_error(ufunc, operands);
}
}
/* 如果 type_num1 不是整数或布尔类型,则返回二元类型解析错误 */
else {
return raise_binary_type_reso_error(ufunc, operands);
}
/* 检查是否符合类型转换规则 */
/* 如果不符合类型转换规则,则释放已分配的内存并返回错误 */
if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
for (i = 0; i < 3; ++i) {
Py_DECREF(out_dtypes[i]);
out_dtypes[i] = NULL;
}
return -1;
}
/* 操作成功完成,返回成功状态 */
return 0;
/*
* This function applies the type resolution rules for subtraction.
* In particular, there are a number of special cases with datetime:
* m8[<A>] - m8[<B>] => m8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)]
* m8[<A>] - int => m8[<A>] - m8[<A>]
* int - m8[<A>] => m8[<A>] - m8[<A>]
* M8[<A>] - int => M8[<A>] - m8[<A>]
* M8[<A>] - m8[<B>] => M8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)]
* TODO: Non-linear time unit cases require highly special-cased loops
* M8[<A>] - m8[Y|M|B]
*/
NPY_NO_EXPORT int
PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int type_num1, type_num2;
int i;
type_num1 = PyArray_DESCR(operands[0])->type_num; // 获取第一个操作数的数据类型编号
type_num2 = PyArray_DESCR(operands[1])->type_num; // 获取第二个操作数的数据类型编号
/* Use the default when datetime and timedelta are not involved */
if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
int ret;
// 如果操作数不涉及 datetime 或 timedelta,则使用默认的类型解析器
ret = PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
if (ret < 0) {
return ret;
}
/* The type resolver would have upcast already */
// 类型解析器应该已经完成了类型提升
if (out_dtypes[0]->type_num == NPY_BOOL) {
PyErr_Format(PyExc_TypeError,
"numpy boolean subtract, the `-` operator, is not supported, "
"use the bitwise_xor, the `^` operator, or the logical_xor "
"function instead.");
return -1;
}
return ret;
}
if (type_num1 == NPY_TIMEDELTA) {
/* m8[<A>] - m8[<B>] => m8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)] */
// 如果第一个操作数是 timedelta
if (type_num2 == NPY_TIMEDELTA) {
// 并且第二个操作数也是 timedelta,则结果类型为两者类型的最小公倍数
out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
PyArray_DESCR(operands[1]));
if (out_dtypes[0] == NULL) {
return -1;
}
// 复制给其他输出类型
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]);
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
}
/* m8[<A>] - int => m8[<A>] - m8[<A>] */
else if (PyTypeNum_ISINTEGER(type_num2) ||
PyTypeNum_ISBOOL(type_num2)) {
// 如果第二个操作数是整数或布尔类型,则结果类型保持为 timedelta
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[0]));
if (out_dtypes[0] == NULL) {
return -1;
}
// 复制给其他输出类型
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]);
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
type_num2 = NPY_TIMEDELTA; // 将第二个操作数类型设置为 timedelta
}
else {
return raise_binary_type_reso_error(ufunc, operands); // 抛出二进制类型解析错误
}
}
else if (type_num1 == NPY_DATETIME) {
/* 如果第一个操作数是日期时间类型 M8[<A>] */
/* M8[<A>] - m8[<B>] => M8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)] */
if (type_num2 == NPY_TIMEDELTA) {
/* 如果第二个操作数是时间增量类型 */
// 推断出输出类型,基于操作数的类型
out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
PyArray_DESCR(operands[1]));
if (out_dtypes[0] == NULL) {
return -1; // 返回错误代码
}
/* 创建一个新的 NPY_TIMEDELTA 类型,并复制日期时间的元数据 */
out_dtypes[1] = timedelta_dtype_with_copied_meta(out_dtypes[0]);
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1; // 返回错误代码
}
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]); // 增加引用计数,防止释放
}
/* M8[<A>] - int => M8[<A>] - m8[<A>] */
else if (PyTypeNum_ISINTEGER(type_num2) ||
PyTypeNum_ISBOOL(type_num2)) {
/* 如果第二个操作数是整数或布尔类型 */
// 确保规范化操作数1的描述符
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[0]));
if (out_dtypes[0] == NULL) {
return -1; // 返回错误代码
}
/* 创建一个新的 NPY_TIMEDELTA 类型,并复制类型1的元数据 */
out_dtypes[1] = timedelta_dtype_with_copied_meta(
PyArray_DESCR(operands[0]));
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1; // 返回错误代码
}
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]); // 增加引用计数,防止释放
type_num2 = NPY_TIMEDELTA; // 设置第二个操作数为时间增量类型
}
/* M8[<A>] - M8[<B>] => M8[gcd(<A>,<B>)] - M8[gcd(<A>,<B>)] */
else if (type_num2 == NPY_DATETIME) {
/* 如果第二个操作数也是日期时间类型 M8[<B>] */
// 推断出输出类型,基于操作数的类型
out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
PyArray_DESCR(operands[1]));
if (out_dtypes[0] == NULL) {
return -1; // 返回错误代码
}
/* 创建一个新的 NPY_TIMEDELTA 类型,并复制类型1的元数据 */
out_dtypes[2] = timedelta_dtype_with_copied_meta(out_dtypes[0]);
if (out_dtypes[2] == NULL) {
Py_DECREF(out_dtypes[0]);
return -1; // 返回错误代码
}
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]); // 增加引用计数,防止释放
}
else {
return raise_binary_type_reso_error(ufunc, operands);
// 如果类型不匹配,则触发二进制类型解析错误
}
}
else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) {
# 如果 type_num1 是整数或布尔类型
/* int - m8[<A>] => m8[<A>] - m8[<A>] */
# 执行 int - m8[<A>] => m8[<A>] - m8[<A>] 的操作,这是一种特定的数学操作符重载形式
if (type_num2 == NPY_TIMEDELTA) {
# 如果 type_num2 是 NPY_TIMEDELTA 类型
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[1]));
# 确保操作数 operands[1] 的描述符是规范化的,并赋给输出类型数组的第一个元素
if (out_dtypes[0] == NULL) {
# 如果未能获取描述符则返回错误
return -1;
}
out_dtypes[1] = out_dtypes[0];
# 输出类型数组的第二个元素等于第一个元素
Py_INCREF(out_dtypes[1]);
# 增加输出类型数组的第二个元素的引用计数
out_dtypes[2] = out_dtypes[0];
# 输出类型数组的第三个元素等于第一个元素
Py_INCREF(out_dtypes[2]);
type_num1 = NPY_TIMEDELTA;
# 将 type_num1 设为 NPY_TIMEDELTA 类型
}
else {
# 否则,如果 type_num2 不是 NPY_TIMEDELTA 类型
return raise_binary_type_reso_error(ufunc, operands);
# 调用函数 raise_binary_type_reso_error 处理二进制操作类型冲突的错误,并返回错误代码
}
}
else {
# 如果 type_num1 不是整数或布尔类型
return raise_binary_type_reso_error(ufunc, operands);
# 同样调用函数 raise_binary_type_reso_error 处理二进制操作类型冲突的错误,并返回错误代码
}
/* Check against the casting rules */
# 检查是否符合类型转换规则
if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
# 如果类型转换验证失败
for (i = 0; i < 3; ++i) {
# 遍历输出类型数组的前三个元素
Py_DECREF(out_dtypes[i]);
# 逐个减少输出类型数组元素的引用计数
out_dtypes[i] = NULL;
# 将输出类型数组元素置为 NULL
}
return -1;
# 返回错误代码
}
return 0;
# 执行成功,返回 0
/*
* This function applies the type resolution rules for multiplication.
* In particular, there are a number of special cases with datetime:
* int## * m8[<A>] => int64 * m8[<A>]
* m8[<A>] * int## => m8[<A>] * int64
* float## * m8[<A>] => float64 * m8[<A>]
* m8[<A>] * float## => m8[<A>] * float64
*/
NPY_NO_EXPORT int
PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int type_num1, type_num2;
int i;
// 获取第一个操作数的数据类型编号
type_num1 = PyArray_DESCR(operands[0])->type_num;
// 获取第二个操作数的数据类型编号
type_num2 = PyArray_DESCR(operands[1])->type_num;
/* 当涉及到 datetime 和 timedelta 时使用默认规则 */
if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)
&& !((PyTypeNum_ISSTRING(type_num1) && PyTypeNum_ISINTEGER(type_num2))
|| (PyTypeNum_ISINTEGER(type_num1) && PyTypeNum_ISSTRING(type_num2)))) {
// 调用默认的类型解析器,用于非特殊情况
return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
}
// 当其中一个操作数是字符串时
if (PyTypeNum_ISSTRING(type_num1) || PyTypeNum_ISSTRING(type_num2)) {
// 如果第一个操作数是字符串
if (PyTypeNum_ISSTRING(type_num1)) {
// 获取第一个操作数的规范描述符,并确保它是规范的
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(PyArray_DESCR(operands[0]));
if (out_dtypes[0] == NULL) {
return -1;
}
// 创建一个新的 int64 类型描述符
out_dtypes[1] = PyArray_DescrNewFromType(NPY_INT64);
if (out_dtypes[1] == NULL) {
return -1;
}
// 对第一个操作数的描述符进行增加引用计数,因为它在 out_dtypes[2] 中被重复使用
// 这里只关注类型而非实际大小
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[0]);
}
// 如果第二个操作数是字符串
else {
// 创建一个新的 int64 类型描述符
out_dtypes[0] = PyArray_DescrNewFromType(NPY_INT64);
if (out_dtypes[0] == NULL) {
return -1;
}
// 获取第二个操作数的规范描述符,并确保它是规范的
out_dtypes[1] = NPY_DT_CALL_ensure_canonical(PyArray_DESCR(operands[1]));
if (out_dtypes[1] == NULL) {
return -1;
}
// 对第二个操作数的描述符进行增加引用计数,因为它在 out_dtypes[2] 中被重复使用
// 这里只关注类型而非实际大小
out_dtypes[2] = out_dtypes[1];
Py_INCREF(out_dtypes[1]);
}
}
else if (type_num1 == NPY_TIMEDELTA) {
/* 如果第一个操作数的类型是时间差类型(NPY_TIMEDELTA) */
/* m8[<A>] * int## => m8[<A>] * int64 */
if (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2)) {
/* 如果第二个操作数的类型是整数或布尔类型 */
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[0]));
/* 确保第一个操作数的数据类型是规范的 */
if (out_dtypes[0] == NULL) {
return -1;
}
out_dtypes[1] = PyArray_DescrNewFromType(NPY_LONGLONG);
/* 创建一个新的数据类型描述符,表示int64类型 */
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1;
}
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
type_num2 = NPY_LONGLONG;
/* 更新第二个操作数的类型为int64 */
}
/* m8[<A>] * float## => m8[<A>] * float64 */
else if (PyTypeNum_ISFLOAT(type_num2)) {
/* 如果第二个操作数的类型是浮点数类型 */
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[0]));
/* 确保第一个操作数的数据类型是规范的 */
if (out_dtypes[0] == NULL) {
return -1;
}
out_dtypes[1] = PyArray_DescrNewFromType(NPY_DOUBLE);
/* 创建一个新的数据类型描述符,表示float64类型 */
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1;
}
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
type_num2 = NPY_DOUBLE;
/* 更新第二个操作数的类型为float64 */
}
else {
return raise_binary_type_reso_error(ufunc, operands);
/* 如果第二个操作数的类型不是整数、布尔或浮点数类型,则引发类型解析错误 */
}
}
else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) {
/* 如果第一个操作数的类型是整数或布尔类型 */
/* int## * m8[<A>] => int64 * m8[<A>] */
if (type_num2 == NPY_TIMEDELTA) {
/* 如果第二个操作数的类型是时间差类型(NPY_TIMEDELTA) */
out_dtypes[0] = PyArray_DescrNewFromType(NPY_LONGLONG);
/* 创建一个新的数据类型描述符,表示int64类型 */
if (out_dtypes[0] == NULL) {
return -1;
}
out_dtypes[1] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[1]));
/* 确保第二个操作数的数据类型是规范的 */
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1;
}
out_dtypes[2] = out_dtypes[1];
Py_INCREF(out_dtypes[2]);
type_num1 = NPY_LONGLONG;
/* 更新第一个操作数的类型为int64 */
}
else {
return raise_binary_type_reso_error(ufunc, operands);
/* 如果第二个操作数的类型不是时间差类型,则引发类型解析错误 */
}
}
# 如果第一个操作数的类型是浮点数(float##),则执行以下操作
else if (PyTypeNum_ISFLOAT(type_num1)) {
/* float## * m8[<A>] => float64 * m8[<A>] */
# 如果第二个操作数的类型是 NPY_TIMEDELTA
if (type_num2 == NPY_TIMEDELTA) {
# 设置输出数据类型为双精度浮点数(NPY_DOUBLE)
out_dtypes[0] = PyArray_DescrNewFromType(NPY_DOUBLE);
# 检查是否成功创建输出数据类型,如果失败则返回错误
if (out_dtypes[0] == NULL) {
return -1;
}
# 确保第二个操作数的数据类型是规范的
out_dtypes[1] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[1]));
# 检查第二个输出数据类型是否有效,如果无效则清理资源并返回错误
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1;
}
# 第三个输出数据类型与第二个相同
out_dtypes[2] = out_dtypes[1];
Py_INCREF(out_dtypes[2]);
# 将第一个操作数的类型设置为双精度浮点数
type_num1 = NPY_DOUBLE;
}
else {
# 如果第二个操作数的类型不是 NPY_TIMEDELTA,则返回二元类型解析错误
return raise_binary_type_reso_error(ufunc, operands);
}
}
else {
# 如果第一个操作数的类型不是浮点数,则返回二元类型解析错误
return raise_binary_type_reso_error(ufunc, operands);
}
/* Check against the casting rules */
# 根据转换规则检查数据类型转换是否有效
if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
# 如果转换无效,清理输出数据类型并返回错误
for (i = 0; i < 3; ++i) {
Py_DECREF(out_dtypes[i]);
out_dtypes[i] = NULL;
}
return -1;
}
# 返回操作成功
return 0;
/*
* This function applies the type resolution rules for division.
* In particular, there are a number of special cases with datetime:
* m8[<A>] / m8[<B>] to m8[gcd(<A>,<B>)] / m8[gcd(<A>,<B>)] -> float64
* m8[<A>] / int## to m8[<A>] / int64 -> m8[<A>]
* m8[<A>] / float## to m8[<A>] / float64 -> m8[<A>]
*/
NPY_NO_EXPORT int
PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int type_num1, type_num2;
int i;
type_num1 = PyArray_DESCR(operands[0])->type_num; // 获取第一个操作数的数据类型编号
type_num2 = PyArray_DESCR(operands[1])->type_num; // 获取第二个操作数的数据类型编号
/* Use the default when datetime and timedelta are not involved */
// 如果操作数不涉及 datetime 和 timedelta 类型,则使用默认类型解析器
if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes); // 调用默认类型解析器并返回结果
}
// 如果涉及 datetime 或 timedelta 类型,则继续下面的类型解析规则处理
# 如果第一个操作数的类型为 NPY_TIMEDELTA
if (type_num1 == NPY_TIMEDELTA) {
"""
* m8[<A>] / m8[<B>] to
* m8[gcd(<A>,<B>)] / m8[gcd(<A>,<B>)] -> float64
"""
# 如果第二个操作数的类型也为 NPY_TIMEDELTA
if (type_num2 == NPY_TIMEDELTA) {
# 计算并设置输出的数据类型,根据两个操作数的类型进行提升
out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
PyArray_DESCR(operands[1]));
# 如果提升类型失败,则返回错误
if (out_dtypes[0] == NULL) {
return -1;
}
# 复制第一个输出类型到第二个输出类型
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]);
"""
* TODO: split function into truediv and floordiv resolvers
"""
# 如果 ufunc 的名称是 "floor_divide",设置第三个输出类型为 NPY_LONGLONG
if (strcmp(ufunc->name, "floor_divide") == 0) {
out_dtypes[2] = PyArray_DescrFromType(NPY_LONGLONG);
}
else {
out_dtypes[2] = PyArray_DescrFromType(NPY_DOUBLE);
}
# 如果获取第三个输出类型失败,则清理之前分配的内存,并返回错误
if (out_dtypes[2] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
Py_DECREF(out_dtypes[1]);
out_dtypes[1] = NULL;
return -1;
}
}
# 如果第二个操作数是整数类型
/* m8[<A>] / int## => m8[<A>] / int64 */
else if (PyTypeNum_ISINTEGER(type_num2)) {
# 确保第一个输出类型为规范类型
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[0]));
# 如果获取失败,则返回错误
if (out_dtypes[0] == NULL) {
return -1;
}
# 设置第二个输出类型为 NPY_LONGLONG
out_dtypes[1] = PyArray_DescrFromType(NPY_LONGLONG);
# 如果获取失败,则清理之前分配的内存,并返回错误
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1;
}
# 复制第一个输出类型到第三个输出类型
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
# 更新第二个操作数的类型为 NPY_LONGLONG
type_num2 = NPY_LONGLONG;
}
# 如果第二个操作数是浮点数类型
/* m8[<A>] / float## => m8[<A>] / float64 */
else if (PyTypeNum_ISFLOAT(type_num2)) {
# 确保第一个输出类型为规范类型
out_dtypes[0] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(operands[0]));
# 如果获取失败,则返回错误
if (out_dtypes[0] == NULL) {
return -1;
}
# 设置第二个输出类型为 NPY_DOUBLE
out_dtypes[1] = PyArray_DescrNewFromType(NPY_DOUBLE);
# 如果获取失败,则清理之前分配的内存,并返回错误
if (out_dtypes[1] == NULL) {
Py_DECREF(out_dtypes[0]);
out_dtypes[0] = NULL;
return -1;
}
# 复制第一个输出类型到第三个输出类型
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
# 更新第二个操作数的类型为 NPY_DOUBLE
type_num2 = NPY_DOUBLE;
}
# 如果第二个操作数类型不符合上述情况,则返回二元类型解析错误
else {
return raise_binary_type_reso_error(ufunc, operands);
}
}
# 如果第一个操作数的类型不是 NPY_TIMEDELTA,则返回二元类型解析错误
else {
return raise_binary_type_reso_error(ufunc, operands);
}
# 检查是否满足类型转换规则
if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
# 如果不满足,则清理所有输出类型并返回错误
for (i = 0; i < 3; ++i) {
Py_DECREF(out_dtypes[i]);
out_dtypes[i] = NULL;
}
return -1;
}
# 操作成功完成,返回 0 表示成功
return 0;
# 定义一个非导出的函数 PyUFunc_RemainderTypeResolver,用于解析 PyUFuncObject 结构的剩余类型
NPY_NO_EXPORT int
PyUFunc_RemainderTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int type_num1, type_num2;
int i;
# 获取第一个操作数的数据类型编号
type_num1 = PyArray_DESCR(operands[0])->type_num;
# 获取第二个操作数的数据类型编号
type_num2 = PyArray_DESCR(operands[1])->type_num;
/* 当没有涉及到 datetime 和 timedelta 时使用默认处理 */
if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
# 调用 PyUFunc_DefaultTypeResolver 处理默认类型解析
return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
}
# 当第一个操作数的类型是 NPY_TIMEDELTA 时
if (type_num1 == NPY_TIMEDELTA) {
# 当第二个操作数的类型也是 NPY_TIMEDELTA 时
if (type_num2 == NPY_TIMEDELTA) {
# 提升第一个操作数和第二个操作数的类型为共同的最小公倍数
out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
PyArray_DESCR(operands[1]));
# 如果无法提升类型,则返回错误
if (out_dtypes[0] == NULL) {
return -1;
}
# 复制结果类型到输出类型数组的其他位置
out_dtypes[1] = out_dtypes[0];
Py_INCREF(out_dtypes[1]);
out_dtypes[2] = out_dtypes[0];
Py_INCREF(out_dtypes[2]);
}
else {
# 如果第二个操作数不是 NPY_TIMEDELTA 类型,则返回二进制类型解析错误
return raise_binary_type_reso_error(ufunc, operands);
}
}
else {
# 如果第一个操作数不是 NPY_TIMEDELTA 类型,则返回二进制类型解析错误
return raise_binary_type_reso_error(ufunc, operands);
}
/* 根据转换规则验证操作是否符合 */
if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
# 如果验证失败,则清除输出类型数组,并返回错误
for (i = 0; i < 3; ++i) {
Py_DECREF(out_dtypes[i]);
out_dtypes[i] = NULL;
}
return -1;
}
# 所有处理完成,返回成功状态
return 0;
}
# 定义一个非导出的函数 PyUFunc_TrueDivisionTypeResolver,用于解析 PyUFuncObject 结构的真除类型
NPY_NO_EXPORT int
PyUFunc_TrueDivisionTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int type_num1, type_num2;
# 获取第一个操作数的数据类型编号
type_num1 = PyArray_DESCR(operands[0])->type_num;
# 获取第二个操作数的数据类型编号
type_num2 = PyArray_DESCR(operands[1])->type_num;
# 当 type_tup 为 NULL 且第一个和第二个操作数都是整数类型或布尔类型时
if (type_tup == NULL &&
(PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) &&
(PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2))) {
# 使用默认的真除类型解析处理
return PyUFunc_DefaultTypeResolver(
ufunc, casting, operands,
npy_static_pydata.default_truediv_type_tup, out_dtypes);
}
# 其他情况使用 PyUFunc_DivisionTypeResolver 处理类型解析
return PyUFunc_DivisionTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
}
# 定义一个静态整型变量
static int
/* 查找用户定义的内循环函数来执行指定的通用函数对象 */
NPY_NO_EXPORT int
PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc,
PyArray_Descr *const *dtypes,
PyUFuncGenericFunction *out_innerloop,
void **out_innerloopdata,
int *out_needs_api)
{
int nargs = ufunc->nargs; // 获取通用函数对象的参数数量
const char *types; // 定义一个字符指针变量 types
int i, j;
/*
* 如果存在用户定义的循环,首先搜索它们。
* TODO: 需要一个循环选择加速结构,比如哈希表。
*/
if (ufunc->userloops) {
switch (find_userloop(ufunc, dtypes,
out_innerloop, out_innerloopdata)) {
/* 错误 */
case -1:
return -1;
/* 找到一个循环 */
case 1:
return 0;
}
}
types = ufunc->types;
// 遍历ufunc对象中的每个类型
for (i = 0; i < ufunc->ntypes; ++i) {
// 将类型复制到一个整数数组以便进行匹配
for (j = 0; j < nargs; ++j) {
// 检查当前参数的类型是否与目标类型匹配
if (types[j] != dtypes[j]->type_num) {
break;
}
}
// 如果所有参数的类型都匹配
if (j == nargs) {
// 设置内部循环函数指针
*out_innerloop = ufunc->functions[i];
// 设置内部循环数据指针,如果数据为空则设为NULL
*out_innerloopdata = (ufunc->data == NULL) ? NULL : ufunc->data[i];
// 返回成功标志
return 0;
}
// 移动到下一组类型
types += nargs;
}
// 如果找不到匹配的内部循环函数,则返回找不到循环错误
return raise_no_loop_found_error(ufunc, (PyObject **)dtypes);
# 定义一个静态函数,用于执行通用函数的循环匹配过程
static int
ufunc_loop_matches(PyUFuncObject *self,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING output_casting,
int any_object,
int use_min_scalar,
int *types, PyArray_Descr **dtypes,
int *out_no_castable_output,
char *out_err_src_typecode,
char *out_err_dst_typecode)
{
# 声明变量 i 用于循环遍历输入和输出操作数,nin 表示输入数量,nop 表示总操作数(输入加输出)
npy_intp i, nin = self->nin, nop = nin + self->nout;
/*
* 首先检查所有输入是否可以安全地转换为此函数所需的类型
*/
for (i = 0; i < nin; ++i) {
PyArray_Descr *tmp;
/*
* 如果没有输入是对象,并且存在多个循环,不允许转换为对象。
* 这主要出于性能考虑。除非只有一个对象参数的内部循环构建的自定义ufunc,
* 否则只实现支持的类型。尝试在浮点参数上使用逻辑或的对象版本似乎不正确。
*/
if (types[i] == NPY_OBJECT && !any_object && self->ntypes > 1) {
return 0;
}
if (types[i] == NPY_NOTYPE) {
continue; /* 通过显式指定匹配 */
}
/*
* 如果类型编号为 NPY_VOID 并且传入了结构 dtypes,则使用结构 dtype 对象。
* 否则,从类型编号创建新的 dtype 对象。
*/
if (types[i] == NPY_VOID && dtypes != NULL) {
tmp = dtypes[i];
Py_INCREF(tmp);
}
else {
tmp = PyArray_DescrFromType(types[i]);
}
if (tmp == NULL) {
return -1;
}
# 如果启用了调试跟踪,则打印详细的类型检查信息
#if NPY_UF_DBG_TRACING
printf("Checking type for op %d, type %d: ", (int)i, (int)types[i]);
PyObject_Print((PyObject *)tmp, stdout, 0);
printf(", operand type: ");
PyObject_Print((PyObject *)PyArray_DESCR(op[i]), stdout, 0);
printf("\n");
#endif
/*
* 如果所有输入都是标量,则使用常规提升规则,而不是特殊的值检查规则。
*/
if (!use_min_scalar) {
if (!PyArray_CanCastTypeTo(PyArray_DESCR(op[i]), tmp,
input_casting)) {
Py_DECREF(tmp);
return 0;
}
}
else {
if (!PyArray_CanCastArrayTo(op[i], tmp, input_casting)) {
Py_DECREF(tmp);
return 0;
}
}
Py_DECREF(tmp);
}
/*
* 如果所有输入都符合要求,则检查是否能够将结果转换为输出类型。
*/
for (i = nin; i < nop; ++i) {
# 循环遍历从 nin 到 nop 的索引范围
if (types[i] == NPY_NOTYPE) {
# 如果 types[i] 等于 NPY_NOTYPE,则跳过当前循环,继续下一个迭代
continue; /* Matched by being explicitly specified. */
}
if (op[i] != NULL) {
# 如果 op[i] 非空
# 根据 types[i] 创建一个新的 PyArray_Descr 对象
PyArray_Descr *tmp = PyArray_DescrFromType(types[i]);
# 如果创建失败,则返回 -1
if (tmp == NULL) {
return -1;
}
# 检查是否可以将 tmp 类型转换为 op[i] 的类型,并且符合输出转换规则 output_casting
if (!PyArray_CanCastTypeTo(tmp, PyArray_DESCR(op[i]),
output_casting)) {
# 如果无法转换
if (!(*out_no_castable_output)) {
# 如果没有设置 *out_no_castable_output 标志,则设置为 1
*out_no_castable_output = 1;
# 设置 *out_err_src_typecode 为 tmp 的类型码
*out_err_src_typecode = tmp->type;
# 设置 *out_err_dst_typecode 为 op[i] 的类型码
*out_err_dst_typecode = PyArray_DESCR(op[i])->type;
}
# 释放 tmp 对象
Py_DECREF(tmp);
# 返回 0 表示无法转换
return 0;
}
# 释放 tmp 对象
Py_DECREF(tmp);
}
}
# 如果循环完成,则返回 1 表示所有操作成功
return 1;
}
static int
set_ufunc_loop_data_types(PyUFuncObject *self, PyArrayObject **op,
PyArray_Descr **out_dtypes,
int *type_nums, PyArray_Descr **dtypes)
{
int i, nin = self->nin, nop = nin + self->nout;
/*
* 填充 dtypes 数组。
* 对于输出变量,
* 还要搜索输入变量以找到匹配的 type_num 来复制,
* 而不是创建一个新的,类似于保留元数据。
**/
for (i = 0; i < nop; ++i) {
if (dtypes != NULL) {
out_dtypes[i] = dtypes[i];
Py_XINCREF(out_dtypes[i]);
/*
* 如果 type_num 匹配,则从 'op' 复制 dtype,
* 以保留元数据。
*/
}
else if (op[i] != NULL &&
PyArray_DESCR(op[i])->type_num == type_nums[i]) {
out_dtypes[i] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(op[i]));
/*
* 对于输出变量,如果 type_num 匹配,则从 op[0] 复制 dtype,
* 类似地保留元数据。
*/
}
else if (i >= nin && op[0] != NULL &&
PyArray_DESCR(op[0])->type_num == type_nums[i]) {
out_dtypes[i] = NPY_DT_CALL_ensure_canonical(
PyArray_DESCR(op[0]));
/* 否则根据 type_nums[i] 创建一个普通的 descr */
}
else {
out_dtypes[i] = PyArray_DescrFromType(type_nums[i]);
}
if (out_dtypes[i] == NULL) {
goto fail;
}
}
return 0;
fail:
while (--i >= 0) {
Py_DECREF(out_dtypes[i]);
out_dtypes[i] = NULL;
}
return -1;
}
/*
* 在参数和循环中进行搜索
*/
static int
linear_search_userloop_type_resolver(PyUFuncObject *self,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING output_casting,
int any_object,
int use_min_scalar,
PyArray_Descr **out_dtype,
int *out_no_castable_output,
char *out_err_src_typecode,
char *out_err_dst_typecode)
{
npy_intp i, nop = self->nin + self->nout;
/* 用于尝试避免重复相同的用户定义循环搜索 */
int last_userdef = -1;
for (i = 0; i < nop; ++i) {
int type_num;
/* 检查是否还有要检查的 ufunc 参数 */
if (op[i] == NULL) {
break;
}
// 获取当前操作数 op[i] 的数据类型编号
type_num = PyArray_DESCR(op[i])->type_num;
// 如果当前数据类型编号不是上一个用户定义的类型,并且是用户定义类型或者是 NPY_VOID 类型
if (type_num != last_userdef &&
(PyTypeNum_ISUSERDEF(type_num) || type_num == NPY_VOID)) {
PyObject *key, *obj;
// 更新上一个用户定义类型为当前类型编号
last_userdef = type_num;
// 创建一个 Python 整数对象 key,表示当前类型编号
key = PyLong_FromLong(type_num);
// 如果创建 key 失败,则返回错误
if (key == NULL) {
return -1;
}
// 在 self->userloops 字典中查找 key 对应的值
obj = PyDict_GetItemWithError(self->userloops, key);
// 减少 key 的引用计数
Py_DECREF(key);
// 如果在查找过程中发生错误,则返回 -1
if (obj == NULL && PyErr_Occurred()) {
return -1;
}
// 如果在字典中没有找到对应的值,继续下一次循环
else if (obj == NULL) {
continue;
}
// 从 obj 中获取 PyUFunc_Loop1d 结构体指针 funcdata
PyUFunc_Loop1d *funcdata = PyCapsule_GetPointer(obj, NULL);
// 如果获取 funcdata 失败,则返回 -1
if (funcdata == NULL) {
return -1;
}
// 遍历 funcdata 链表,查找匹配的循环函数数据
for (; funcdata != NULL; funcdata = funcdata->next) {
int *types = funcdata->arg_types;
// 根据一些条件判断是否匹配当前的循环函数
switch (ufunc_loop_matches(self, op,
input_casting, output_casting,
any_object, use_min_scalar,
types, funcdata->arg_dtypes,
out_no_castable_output, out_err_src_typecode,
out_err_dst_typecode)) {
// 发生错误
case -1:
return -1;
// 找到匹配的循环函数
case 1:
// 设置 ufunc 循环的数据类型
set_ufunc_loop_data_types(self, op, out_dtype, types, funcdata->arg_dtypes);
return 1;
}
}
}
}
/* 没有找到匹配的循环函数 */
return 0;
}
/*
* 这是一个静态函数,用于在给定的PyUFuncObject上执行类型元组解析器的搜索。
* 它根据传入的参数和循环进行搜索。
*/
static int
type_tuple_userloop_type_resolver(PyUFuncObject *self,
int n_specified,
int *specified_types,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING casting,
int any_object,
int use_min_scalar,
PyArray_Descr **out_dtype)
{
int i, j, nin = self->nin, nop = nin + self->nout;
assert(n_specified == nop);
int types[NPY_MAXARGS];
/* 用于尝试避免重复搜索相同的用户定义循环 */
int last_userdef = -1;
int no_castable_output = 0;
char err_src_typecode = '-', err_dst_typecode = '-';
}
/* 没有找到匹配 */
return 0;
}
/*
* 执行ufunc的最佳内部循环的线性搜索。
*
* 注意,如果返回错误,调用者必须释放out_dtype中的非零引用。
* 这个函数本身不负责清理。
*/
NPY_NO_EXPORT int
linear_search_type_resolver(PyUFuncObject *self,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING output_casting,
int any_object,
PyArray_Descr **out_dtype)
{
npy_intp i, j, nin = self->nin, nop = nin + self->nout;
int types[NPY_MAXARGS];
const char *ufunc_name;
int no_castable_output = 0;
/* 用于在强制错误时生成更好的错误消息 */
char err_dst_typecode = '-', err_src_typecode = '-';
ufunc_name = ufunc_get_name_cstr(self);
int promotion_state = get_npy_promotion_state();
assert(promotion_state != NPY_USE_WEAK_PROMOTION_AND_WARN);
/* 对于Python int/float/complex,总是使用新的提升 */
int use_min_scalar;
if (promotion_state == NPY_USE_LEGACY_PROMOTION) {
use_min_scalar = should_use_min_scalar(nin, op, 0, NULL);
}
else {
use_min_scalar = should_use_min_scalar_weak_literals(nin, op);
}
/* 如果ufunc具有用户定义的循环,则搜索它们 */
if (self->userloops) {
switch (linear_search_userloop_type_resolver(self, op,
input_casting, output_casting,
any_object, use_min_scalar, out_dtype,
&no_castable_output, &err_src_typecode,
&err_dst_typecode)) {
/* 错误 */
case -1:
return -1;
/* 找到了一个循环 */
case 1:
return 0;
}
}
/*
* 确定 UFunc 循环。一般情况下,这可能会更快,更好的实现方式可能是让 ufunc
* 提供一个函数,返回结果类型和内部循环函数。
*
* 对于遵循最典型模式的函数,可以提供默认的快速机制,当所有函数的签名为 "xx...x -> x"
* 对于某个内置数据类型 x 时,按如下方式操作:
* - 使用 PyArray_ResultType 获取输出类型
* - 根据输出类型编号在表中查找内部循环
*
* 在前面代码中找到循环的方法似乎不一致(如 np.add 生成的强制转换表格中的某些不对称性)。
*/
no_castable_output = 0; // 初始化没有可转换输出的标志为 0
for (i = 0; i < self->ntypes; ++i) { // 遍历每种类型
const char *orig_types = self->types + i*self->nargs; // 指向原始类型的指针
/* 将类型复制到一个整数数组以进行匹配 */
for (j = 0; j < nop; ++j) {
types[j] = orig_types[j]; // 复制类型到匹配数组
}
switch (ufunc_loop_matches(self, op,
input_casting, output_casting,
any_object, use_min_scalar,
types, NULL,
&no_castable_output, &err_src_typecode,
&err_dst_typecode)) {
/* 出错 */
case -1:
return -1; // 返回错误代码
/* 找到匹配 */
case 1:
set_ufunc_loop_data_types(self, op, out_dtype, types, NULL); // 设置 UFunc 循环的数据类型
return 0; // 返回成功代码
}
}
/* 如果找不到函数,抛出错误 */
if (no_castable_output) {
PyErr_Format(PyExc_TypeError,
"ufunc '%s' 输出(类型码 '%c')无法强制转换为提供的输出参数 "
"(类型码 '%c'),根据强制转换规则 '%s'",
ufunc_name, err_src_typecode, err_dst_typecode,
npy_casting_to_string(output_casting));
}
else {
/*
* TODO: 如果强制转换规则是 same_kind 或 unsafe,应该再次尝试,并更宽松地查找函数。
*/
PyErr_Format(PyExc_TypeError,
"ufunc '%s' 不支持输入类型,并且根据强制转换规则 '%s' 无法安全地将输入强制转换为任何支持的类型",
ufunc_name,
npy_casting_to_string(input_casting));
}
return -1; // 返回错误代码
static int
type_tuple_type_resolver_core(PyUFuncObject *self,
PyArrayObject **op,
NPY_CASTING input_casting, NPY_CASTING casting,
int specified_types[],
int any_object,
int no_castable_output, int use_min_scalar,
PyArray_Descr **out_dtype)
{
int i, j;
int nop = self->nargs;
int types[NPY_MAXARGS];
/* For making a better error message on coercion error */
// 定义错误目标和源类型码的字符变量,用于更好的错误消息
char err_dst_typecode = '-', err_src_typecode = '-';
/* If the ufunc has userloops, search for them. */
// 如果ufunc有用户自定义循环,进行搜索
if (self->userloops) {
switch (type_tuple_userloop_type_resolver(self,
nop, specified_types,
op, input_casting, casting,
any_object, use_min_scalar,
out_dtype)) {
/* Error */
// 错误情况
case -1:
return -1;
/* Found matching loop */
// 找到匹配的循环
case 1:
return 0;
}
}
for (i = 0; i < self->ntypes; ++i) {
const char *orig_types = self->types + i*self->nargs;
/*
* Check specified types and copy into an int array for matching
* (Mostly duplicated in `type_tuple_userloop_type_resolver`)
*/
// 检查指定的类型并复制到一个int数组中进行匹配
for (j = 0; j < nop; ++j) {
if (specified_types[j] == NPY_NOTYPE) {
types[j] = orig_types[j];
continue;
}
if (orig_types[j] != specified_types[j]) {
break;
}
/* indicate that we do not have to check this type anymore. */
// 表示我们不需要再检查这种类型了
types[j] = NPY_NOTYPE;
}
if (j < nop) {
/* no match */
// 没有匹配项
continue;
}
switch (ufunc_loop_matches(self, op,
input_casting, casting,
any_object, use_min_scalar,
types, NULL,
&no_castable_output, &err_src_typecode,
&err_dst_typecode)) {
case -1:
/* Error */
// 错误情况
return -1;
case 0:
/* Cannot cast inputs */
// 无法转换输入
continue;
case 1:
/* Success, fill also the NPY_NOTYPE (cast from char to int) */
// 成功,还填充NPY_NOTYPE(从char到int的转换)
for (j = 0; j < nop; j++) {
types[j] = orig_types[j];
}
set_ufunc_loop_data_types(self, op, out_dtype, types, NULL);
/* In principle, we only need to validate the NPY_NOTYPE ones */
// 原则上,我们只需要验证NPY_NOTYPE的情况
if (PyUFunc_ValidateCasting(self, casting, op, out_dtype) < 0) {
for (j = 0; j < self->nargs; j++) {
Py_DECREF(out_dtype[j]);
out_dtype[j] = NULL;
}
return -1;
}
return 0;
}
}
return -2;
}
/*
* 在给定的 `type_tup` 中执行 ufunc 的线性搜索。
* 如果返回错误,调用者必须释放 `out_dtype` 中的非零引用。该函数不负责清理工作。
*/
NPY_NO_EXPORT int
type_tuple_type_resolver(PyUFuncObject *self,
PyObject *type_tup,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING casting,
int any_object,
PyArray_Descr **out_dtype)
{
// 获取输入参数个数 nin 和总参数个数 nop
int nin = self->nin, nop = nin + self->nout;
// 初始化指定类型数组
int specified_types[NPY_MAXARGS];
// 获取 ufunc 名称
const char *ufunc_name;
ufunc_name = ufunc_get_name_cstr(self);
// 获取 Numpy 推广状态
int promotion_state = get_npy_promotion_state();
// 断言推广状态不为 NPY_USE_WEAK_PROMOTION_AND_WARN
assert(promotion_state != NPY_USE_WEAK_PROMOTION_AND_WARN);
// 始终使用新的推广方式以确保兼容 Python 的 int/float/complex 类型
int use_min_scalar;
if (promotion_state == NPY_USE_LEGACY_PROMOTION) {
use_min_scalar = should_use_min_scalar(nin, op, 0, NULL);
}
else {
use_min_scalar = should_use_min_scalar_weak_literals(nin, op);
}
// 从元组或字符串中填充指定的类型
const char *bad_type_tup_msg = (
"Only NumPy must call `ufunc->type_resolver()` explicitly. "
"NumPy ensures that a type-tuple is normalized now to be a tuple "
"only containing None or descriptors. If anything else is passed "
"(you are seeing this message), the `type_resolver()` was called "
"directly by a third party. "
"This is unexpected, please inform the NumPy developers about it. "
"Also note that `type_resolver` will be phased out, since it must "
"be replaced.");
if (PyTuple_CheckExact(type_tup)) {
Py_ssize_t n = PyTuple_GET_SIZE(type_tup);
if (n != nop) {
PyErr_SetString(PyExc_RuntimeError, bad_type_tup_msg);
return -1;
}
for (int i = 0; i < nop; ++i) {
PyObject *item = PyTuple_GET_ITEM(type_tup, i);
if (item == Py_None) {
specified_types[i] = NPY_NOTYPE;
}
else {
if (!PyArray_DescrCheck(item)) {
PyErr_SetString(PyExc_RuntimeError, bad_type_tup_msg);
return -1;
}
specified_types[i] = ((PyArray_Descr *)item)->type_num;
}
}
}
else {
PyErr_SetString(PyExc_RuntimeError, bad_type_tup_msg);
return -1;
}
// 调用核心的类型解析器函数
int res = type_tuple_type_resolver_core(self,
op, input_casting, casting, specified_types, any_object,
no_castable_output, use_min_scalar, out_dtype);
// 如果结果不为 -2,直接返回结果
if (res != -2) {
return res;
}
/*
* 如果用户传递了 `dtype=dtype`,它会被转换为 `signature=(None,)*nin + (dtype,)*nout`。
* 如果签名完全匹配(可以放宽,但对于向后兼容性来说不是必须的),
* 我们也尝试 `signature=(dtype,)*(nin+nout)`。
* 由于 reduction 传递中使用 `(dtype, None, dtype)`,我们将所有未指定的 dtype 替换为同类输出类型。
* 注意,这可能(通常会)导致不安全的类型转换。通常情况下会拒绝这样的转换(但目前不适用于 reductions)。
* 这曾经是 `dtype=dtype` 的主要含义,但一些调用打破了这种期望,改变它允许将来对像 `np.ldexp` 这样的 ufuncs 有用,
* 同时也在早期将其规范化为一个 `signature`。
*/
int homogeneous_type = NPY_NOTYPE;
if (self->nout > 0) {
homogeneous_type = specified_types[nin];
for (int i = nin+1; i < nop; i++) {
if (specified_types[i] != homogeneous_type) {
homogeneous_type = NPY_NOTYPE;
break;
}
}
}
if (homogeneous_type != NPY_NOTYPE) {
for (int i = 0; i < nin; i++) {
if (specified_types[i] != NPY_NOTYPE) {
/* 永远不要替换已指定的类型! */
continue;
}
specified_types[i] = homogeneous_type;
}
/* 使用同类指定的类型再次尝试。 */
res = type_tuple_type_resolver_core(self,
op, input_casting, casting, specified_types, any_object,
no_castable_output, use_min_scalar, out_dtype);
if (res != -2) {
return res;
}
}
/* 如果找不到匹配指定签名和转换方式的循环函数,则抛出错误 */
PyErr_Format(PyExc_TypeError,
"No loop matching the specified signature and casting "
"was found for ufunc %s", ufunc_name);
return -1;
NPY_NO_EXPORT int
PyUFunc_DivmodTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int type_num1, type_num2;
int i;
// 获取第一个操作数的类型编号
type_num1 = PyArray_DESCR(operands[0])->type_num;
// 获取第二个操作数的类型编号
type_num2 = PyArray_DESCR(operands[1])->type_num;
/* 当涉及到 datetime 和 timedelta 类型时使用默认解析器 */
if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
// 返回默认类型解析器的结果
return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
}
// 若第一个类型是 timedelta
if (type_num1 == NPY_TIMEDELTA) {
// 若第二个类型也是 timedelta
if (type_num2 == NPY_TIMEDELTA) {
// 选择并推广操作数的类型以便匹配
out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
PyArray_DESCR(operands[1]));
// 第二个返回类型等同于第一个
out_dtypes[1] = out_dtypes[0];
// 增加第二个返回类型的引用计数
Py_INCREF(out_dtypes[1]);
// 第三个返回类型是 NPY_LONGLONG
out_dtypes[2] = PyArray_DescrFromType(NPY_LONGLONG);
// 第四个返回类型等同于第一个
out_dtypes[3] = out_dtypes[0];
// 增加第四个返回类型的引用计数
Py_INCREF(out_dtypes[3]);
}
else {
// 若第二个类型不是 timedelta,则引发二进制类型解析错误
return raise_binary_type_reso_error(ufunc, operands);
}
}
else {
// 若第一个类型不是 timedelta,则引发二进制类型解析错误
return raise_binary_type_reso_error(ufunc, operands);
}
/* 根据转换规则检查 */
if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
// 若转换无效,则释放所有返回类型并返回错误
for (i = 0; i < 4; ++i) {
Py_DECREF(out_dtypes[i]);
out_dtypes[i] = NULL;
}
return -1;
}
// 返回成功
return 0;
}
.\numpy\numpy\_core\src\umath\ufunc_type_resolution.h
// 声明 PyUFunc_SimpleBinaryComparisonTypeResolver 函数,用于解析简单二元比较运算的类型
NPY_NO_EXPORT int
PyUFunc_SimpleBinaryComparisonTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
// 声明 PyUFunc_NegativeTypeResolver 函数,用于解析负数运算的类型
NPY_NO_EXPORT int
PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
// 声明 PyUFunc_OnesLikeTypeResolver 函数,用于解析生成全1数组运算的类型
NPY_NO_EXPORT int
PyUFunc_OnesLikeTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
// 声明 PyUFunc_SimpleUniformOperationTypeResolver 函数,用于解析简单统一操作运算的类型
NPY_NO_EXPORT int
PyUFunc_SimpleUniformOperationTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
// 声明 PyUFunc_AbsoluteTypeResolver 函数,用于解析绝对值运算的类型
NPY_NO_EXPORT int
PyUFunc_AbsoluteTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
// 声明 PyUFunc_IsNaTTypeResolver 函数,用于解析是否为 NaT(Not a Time)运算的类型
NPY_NO_EXPORT int
PyUFunc_IsNaTTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
// 声明 PyUFunc_IsFiniteTypeResolver 函数,用于解析是否为有限数运算的类型
NPY_NO_EXPORT int
PyUFunc_IsFiniteTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
// 声明 PyUFunc_AdditionTypeResolver 函数,用于解析加法运算的类型
NPY_NO_EXPORT int
PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
// 声明 PyUFunc_SubtractionTypeResolver 函数,用于解析减法运算的类型
NPY_NO_EXPORT int
PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
NPY_NO_EXPORT int
PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
NPY_NO_EXPORT int
PyUFunc_TrueDivisionTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
NPY_NO_EXPORT int
PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
NPY_NO_EXPORT int
PyUFunc_RemainderTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
NPY_NO_EXPORT int
PyUFunc_DivmodTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
/*
* 执行对ufunc的最佳内部循环的线性搜索。
*
* 注意,如果返回错误,则调用者必须释放out_dtype中的非零引用。
* 此函数不执行自身的清理工作。
*/
NPY_NO_EXPORT int
linear_search_type_resolver(PyUFuncObject *self,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING output_casting,
int any_object,
PyArray_Descr **out_dtype);
/*
* 对由type_tup指定的ufunc的内部循环执行线性搜索。
*
* 注意,如果返回错误,则调用者必须释放out_dtype中的非零引用。
* 此函数不执行自身的清理工作。
*/
NPY_NO_EXPORT int
type_tuple_type_resolver(PyUFuncObject *self,
PyObject *type_tup,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING casting,
int any_object,
PyArray_Descr **out_dtype);
// 定义一个名为 PyUFunc_DefaultLegacyInnerLoopSelector 的函数,接受多个参数:
// - ufunc: PyUFuncObject 类型的指针,代表一个通用函数对象
// - dtypes: PyArray_Descr 类型指针的数组,表示数据类型描述符的数组
// - out_innerloop: 指向 PyUFuncGenericFunction 函数指针的指针,用于存储选定的内部循环函数
// - out_innerloopdata: void 类型指针的指针,用于存储与选定内部循环函数相关的数据
// - out_needs_api: 整数指针,用于指示是否需要 API 支持
PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc,
PyArray_Descr *const *dtypes,
PyUFuncGenericFunction *out_innerloop,
void **out_innerloopdata,
int *out_needs_api);
// NPY_NO_EXPORT 是一个宏,用于指示这个函数不会被导出给外部使用,仅在当前模块内部可见
// 定义一个名为 raise_no_loop_found_error 的函数,接受两个参数:
// - ufunc: PyUFuncObject 类型的指针,代表一个通用函数对象
// - dtypes: PyObject 类型的指针的指针,表示数据类型对象的指针
// 函数用途是在未找到匹配循环时引发错误
NPY_NO_EXPORT int
raise_no_loop_found_error(PyUFuncObject *ufunc, PyObject **dtypes);
// 结束条件编译指令
.\numpy\numpy\_core\src\umath\umathmodule.c
/*
* _UMATHMODULE IS needed in __ufunc_api.h, included from numpy/ufuncobject.h.
* This is a mess and it would be nice to fix it. It has nothing to do with
* __ufunc_api.c
*/
/* 定义宏 _UMATHMODULE,用于 __ufunc_api.h 中,从 numpy/ufuncobject.h 中引入 */
/* 使用最新的 NPY API 版本,禁用所有过时的 API */
/* 定义宏 _MULTIARRAYMODULE,用于多维数组模块 */
/* 定义宏 _UMATHMODULE,用于通用数学函数模块 */
/* 清除 PY_SSIZE_T_CLEAN 宏定义 */
/* 包含 Python 标准头文件 */
/* 包含 numpy 的配置文件 */
/* 包含 numpy CPU 特性检测和分发 */
/* 包含 numpy CPU 相关头文件 */
/* 包含 numpy 数组对象头文件 */
/* 包含 numpy 通用函数对象头文件 */
/* 包含 numpy 3k 兼容性头文件 */
/* 包含 numpy Python 兼容性头文件 */
/* 包含 numpy 抽象对象头文件 */
/* 包含 numpy 数学函数头文件 */
/* 包含数字相关头文件 */
/* 包含分发相关头文件 */
/* 包含字符串通用函数头文件 */
/* 包含字符串数据类型通用函数头文件 */
/* 包含特殊整数比较头文件 */
/* 包含外部对象头文件,用于 _extobject_contextvar 的暴露 */
/* 包含通用函数类型解析头文件 */
/* 包含 funcs.inc 自动生成的所有通用函数的代码 */
/* 包含 __umath_generated.c 自动生成的代码 */
/* 定义 pyfunc_functions 数组,包含 PyUFunc_On_Om 函数 */
static PyUFuncGenericFunction pyfunc_functions[] = {PyUFunc_On_Om};
/* 定义对象类型解析函数 object_ufunc_type_resolver */
static int
object_ufunc_type_resolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
int i, nop = ufunc->nin + ufunc->nout;
/* 将输出数据类型设置为 NPY_OBJECT 类型 */
out_dtypes[0] = PyArray_DescrFromType(NPY_OBJECT);
if (out_dtypes[0] == NULL) {
return -1;
}
/* 复制第一个输出数据类型给所有操作数 */
for (i = 1; i < nop; ++i) {
Py_INCREF(out_dtypes[0]);
out_dtypes[i] = out_dtypes[0];
}
return 0;
}
/* 定义从 Python 函数创建通用函数的函数 ufunc_frompyfunc */
PyObject *
ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) {
PyObject *function, *pyname = NULL;
int nin, nout, i, nargs;
PyUFunc_PyFuncData *fdata;
PyUFuncObject *self;
const char *fname = NULL;
char *str, *types, *doc;
Py_ssize_t fname_len = -1;
void * ptr, **data;
int offset[2];
PyObject *identity = NULL; /* 注意:语义不同于 Py_None */
static char *kwlist[] = {"", "nin", "nout", "identity", NULL};
/* 解析参数列表和关键字参数 */
if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oii|$O:frompyfunc", kwlist,
&function, &nin, &nout, &identity)) {
return NULL;
}
/* 检查 function 是否可调用 */
if (!PyCallable_Check(function)) {
PyErr_SetString(PyExc_TypeError, "function must be callable");
return NULL;
}
/* 计算参数个数 */
nargs = nin + nout;
/* 获取 function 的名称 */
pyname = PyObject_GetAttrString(function, "__name__");
if (pyname) {
fname = PyUnicode_AsUTF8AndSize(pyname, &fname_len);
}
/* 如果获取名称失败,则使用默认名称 "?" */
if (fname == NULL) {
PyErr_Clear();
fname = "?";
fname_len = 1;
}
/* 这里为函数体的起始部分,后续代码未提供,可能包括函数体实现和返回语句 */
}
/*
* ptr will be assigned to self->ptr, which holds a pointer to memory allocated for various data structures:
* self->data[0] (fdata)
* self->data
* self->name
* self->types
*
* To ensure memory alignment on void * pointers, additional space may be allocated.
* Therefore, offsets are calculated to ensure alignment.
*/
offset[0] = sizeof(PyUFunc_PyFuncData);
i = (sizeof(PyUFunc_PyFuncData) % sizeof(void *));
if (i) {
offset[0] += (sizeof(void *) - i);
}
offset[1] = nargs;
i = (nargs % sizeof(void *));
if (i) {
offset[1] += (sizeof(void *) - i);
}
// Allocate memory for ptr, considering offsets and additional space for fname.
ptr = PyArray_malloc(offset[0] + offset[1] + sizeof(void *) + (fname_len + 14));
if (ptr == NULL) {
Py_XDECREF(pyname);
return PyErr_NoMemory();
}
// Point fdata to the allocated memory at ptr.
fdata = (PyUFunc_PyFuncData *)(ptr);
fdata->callable = function;
fdata->nin = nin;
fdata->nout = nout;
// Set data to point to fdata and initialize types array.
data = (void **)(((char *)ptr) + offset[0]);
data[0] = (void *)fdata;
types = (char *)data + sizeof(void *);
for (i = 0; i < nargs; i++) {
types[i] = NPY_OBJECT;
}
// Set str to contain fname followed by " (vectorized)".
str = types + offset[1];
memcpy(str, fname, fname_len);
memcpy(str + fname_len, " (vectorized)", 14);
Py_XDECREF(pyname);
/* Do a better job someday */
doc = "dynamic ufunc based on a python function";
// Create a PyUFuncObject using PyUFunc_FromFuncAndDataAndSignatureAndIdentity.
self = (PyUFuncObject *)PyUFunc_FromFuncAndDataAndSignatureAndIdentity(
(PyUFuncGenericFunction *)pyfunc_functions, data,
types, /* ntypes */ 1, nin, nout, identity ? PyUFunc_IdentityValue : PyUFunc_None,
str, doc, /* unused */ 0, NULL, identity);
if (self == NULL) {
PyArray_free(ptr);
return NULL;
}
// Increment the reference count for function, assign object and ptr to self.
Py_INCREF(function);
self->obj = function;
self->ptr = ptr;
// Set type_resolver and track self for garbage collection.
self->type_resolver = &object_ufunc_type_resolver;
PyObject_GC_Track(self);
// Return the constructed PyUFuncObject.
return (PyObject *)self;
/* docstring in numpy.add_newdocs.py */
/* 定义一个函数,用于为新的ufunc对象添加文档字符串 */
PyObject *
add_newdoc_ufunc(PyObject *NPY_UNUSED(dummy), PyObject *args)
{
// 声明变量
PyUFuncObject *ufunc;
PyObject *str;
// 解析传入的参数,期望参数为 PyUFunc_Type 和 PyUnicode_Type
if (!PyArg_ParseTuple(args, "O!O!:_add_newdoc_ufunc", &PyUFunc_Type, &ufunc,
&PyUnicode_Type, &str)) {
return NULL;
}
// 如果ufunc对象已经有文档字符串,不允许修改,返回错误
if (ufunc->doc != NULL) {
PyErr_SetString(PyExc_ValueError,
"Cannot change docstring of ufunc with non-NULL docstring");
return NULL;
}
// 将传入的字符串对象转换为UTF-8编码的字节串
PyObject *tmp = PyUnicode_AsUTF8String(str);
if (tmp == NULL) {
return NULL;
}
char *docstr = PyBytes_AS_STRING(tmp);
/*
* 这里存在内存泄漏风险,因为分配的文档字符串内存不会在删除ufunc对象时被释放。
* 不过通常情况下不会有问题,因为用户需要重复创建、文档化和丢弃ufunc对象才会触发。
*/
// 为新文档字符串分配内存空间,并复制内容
char *newdocstr = malloc(strlen(docstr) + 1);
if (!newdocstr) {
Py_DECREF(tmp);
return PyErr_NoMemory();
}
strcpy(newdocstr, docstr);
// 将新的文档字符串赋值给ufunc对象
ufunc->doc = newdocstr;
// 释放临时字节串对象
Py_DECREF(tmp);
// 返回None表示成功
Py_RETURN_NONE;
}
/*
*****************************************************************************
** SETUP UFUNCS **
*****************************************************************************
*/
/* 设置ufunc模块的初始化函数 */
int initumath(PyObject *m)
{
// 声明变量
PyObject *d, *s, *s2;
int UFUNC_FLOATING_POINT_SUPPORT = 1;
// 检查是否定义了 NO_UFUNC_FLOATING_POINT_SUPPORT 宏
UFUNC_FLOATING_POINT_SUPPORT = 0;
// 将模块m的字典对象赋值给变量d
d = PyModule_GetDict(m);
// 初始化操作符,如果失败则返回-1
if (InitOperators(d) < 0) {
return -1;
}
// 向模块字典中添加一些符号常量
PyDict_SetItemString(d, "pi", s = PyFloat_FromDouble(NPY_PI));
Py_DECREF(s);
PyDict_SetItemString(d, "e", s = PyFloat_FromDouble(NPY_E));
Py_DECREF(s);
PyDict_SetItemString(d, "euler_gamma", s = PyFloat_FromDouble(NPY_EULER));
Py_DECREF(s);
// 定义宏来添加整型常量和字符串常量
// 添加浮点异常常量
ADDCONST(FPE_DIVIDEBYZERO);
ADDCONST(FPE_OVERFLOW);
ADDCONST(FPE_UNDERFLOW);
ADDCONST(FPE_INVALID);
// 添加浮点支持宏
ADDCONST(FLOATING_POINT_SUPPORT);
// 添加Python值的名称常量
ADDSCONST(PYVALS_NAME);
// 清除宏定义
// 添加整型常量 UFUC_BUFSIZE_DEFAULT
PyModule_AddIntConstant(m, "UFUNC_BUFSIZE_DEFAULT", (long)NPY_BUFSIZE);
// 增加对静态数据的引用,并添加到模块中
Py_INCREF(npy_static_pydata.npy_extobj_contextvar);
PyModule_AddObject(m, "_extobj_contextvar", npy_static_pydata.npy_extobj_contextvar);
// 向模块添加一些浮点数对象常量
PyModule_AddObject(m, "PINF", PyFloat_FromDouble(NPY_INFINITY));
PyModule_AddObject(m, "NINF", PyFloat_FromDouble(-NPY_INFINITY));
PyModule_AddObject(m, "PZERO", PyFloat_FromDouble(NPY_PZERO));
PyModule_AddObject(m, "NZERO", PyFloat_FromDouble(NPY_NZERO));
PyModule_AddObject(m, "NAN", PyFloat_FromDouble(NPY_NAN));
}
s = PyDict_GetItemString(d, "divide");
PyDict_SetItemString(d, "true_divide", s);
s = PyDict_GetItemString(d, "conjugate");
s2 = PyDict_GetItemString(d, "remainder");
if (_PyArray_SetNumericOps(d) < 0) {
return -1;
}
PyDict_SetItemString(d, "conj", s);
PyDict_SetItemString(d, "mod", s2);
"""
* 设置逻辑函数的提升器
* TODO: 可能应该在更合适的位置进行,甚至可以直接在代码生成器中完成。
"""
int res = PyDict_GetItemStringRef(d, "logical_and", &s);
if (res <= 0) {
return -1;
}
if (install_logical_ufunc_promoter(s) < 0) {
Py_DECREF(s);
return -1;
}
Py_DECREF(s);
res = PyDict_GetItemStringRef(d, "logical_or", &s);
if (res <= 0) {
return -1;
}
if (install_logical_ufunc_promoter(s) < 0) {
Py_DECREF(s);
return -1;
}
Py_DECREF(s);
res = PyDict_GetItemStringRef(d, "logical_xor", &s);
if (res <= 0) {
return -1;
}
if (install_logical_ufunc_promoter(s) < 0) {
Py_DECREF(s);
return -1;
}
Py_DECREF(s);
if (init_string_ufuncs(d) < 0) {
return -1;
}
if (init_stringdtype_ufuncs(m) < 0) {
return -1;
}
if (init_special_int_comparisons(d) < 0) {
return -1;
}
return 0;
}
.\numpy\numpy\_core\src\umath\wrapping_array_method.c
/*
* This file defines most of the machinery in order to wrap an existing ufunc
* loop for use with a different set of dtypes.
*
* There are two approaches for this, one is to teach the NumPy core about
* the possibility that the loop descriptors do not match exactly the result
* descriptors.
* The other is to handle this fully by "wrapping", so that NumPy core knows
* nothing about this going on.
* The slight difficulty here is that `context` metadata needs to be mutated.
* It also adds a tiny bit of overhead, since we have to "fix" the descriptors
* and unpack the auxdata.
*
* This means that this currently needs to live within NumPy, as it needs both
* extensive API exposure to do it outside, as well as some thoughts on how to
* expose the `context` without breaking ABI forward compatibility.
* (I.e. we probably need to allocate the context and provide a copy function
* or so.)
*/
/*
* Function: wrapping_method_resolve_descriptors
* ---------------------------------------------
* Resolve descriptors for a wrapped ufunc method, handling dtype translation
* and context mutation.
*
* Parameters:
* - self: PyArrayMethodObject instance representing the wrapped ufunc method
* - dtypes: Array of PyArray_DTypeMeta pointers
* - given_descrs: Array of given descriptors
* - loop_descrs: Output array for loop descriptors
* - view_offset: View offset as npy_intp pointer
*
* Returns:
* - NPY_CASTING value indicating the casting behavior
*/
static NPY_CASTING
wrapping_method_resolve_descriptors(
PyArrayMethodObject *self,
PyArray_DTypeMeta *const dtypes[],
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[],
npy_intp *view_offset)
{
int nin = self->nin, nout = self->nout, nargs = nin + nout;
PyArray_Descr *orig_given_descrs[NPY_MAXARGS];
PyArray_Descr *orig_loop_descrs[NPY_MAXARGS];
// Translate given descriptors using wrapped method's types
if (self->translate_given_descrs(
nin, nout, self->wrapped_dtypes,
given_descrs, orig_given_descrs) < 0) {
return -1;
}
// Resolve descriptors using wrapped method, obtain casting behavior
NPY_CASTING casting = self->wrapped_meth->resolve_descriptors(
self->wrapped_meth, self->wrapped_dtypes,
orig_given_descrs, orig_loop_descrs, view_offset);
// Release original given descriptors
for (int i = 0; i < nargs; i++) {
Py_XDECREF(orig_given_descrs[i]);
}
// Return immediately if resolve_descriptors failed
if (casting < 0) {
return -1;
}
// Translate loop descriptors for the wrapped method
int res = self->translate_loop_descrs(
nin, nout, dtypes, given_descrs, orig_loop_descrs, loop_descrs);
// Release original loop descriptors
for (int i = 0; i < nargs; i++) {
Py_DECREF(orig_loop_descrs[i]);
}
// Return immediately if translate_loop_descrs failed
if (res < 0) {
return -1;
}
return casting;
}
/*
* Structure: wrapping_auxdata
* ---------------------------
* Auxiliary data structure for wrapping ufunc method, encapsulating original
* context, loop, auxdata, and descriptors.
*/
typedef struct {
NpyAuxData base;
PyArrayMethod_Context orig_context; // Original method context
PyArrayMethod_StridedLoop *orig_loop; // Original strided loop
NpyAuxData *orig_auxdata; // Original auxiliary data
PyArray_Descr *descriptors[NPY_MAXARGS]; // Array of descriptors
} wrapping_auxdata;
#define WRAPPING_AUXDATA_FREELIST_SIZE 5
static int wrapping_auxdata_freenum = 0;
static wrapping_auxdata *wrapping_auxdata_freelist[WRAPPING_AUXDATA_FREELIST_SIZE] = {NULL};
/*
* Function: wrapping_auxdata_free
* -------------------------------
* Free function for releasing wrapping_auxdata resources.
*
* Parameters:
* - wrapping_auxdata: Pointer to wrapping_auxdata instance to be freed
*/
static void
wrapping_auxdata_free(wrapping_auxdata *wrapping_auxdata)
{
/* Free auxdata, everything else is borrowed: */
/* Free the wrapping_auxdata instance */
/* Release the base NpyAuxData */
NPY_AUXDATA_FREE((NpyAuxData *)wrapping_auxdata);
/* Reset the original context and loop pointers to NULL */
wrapping_auxdata->orig_context = NULL;
wrapping_auxdata->orig_loop = NULL;
/* Release the original auxiliary data if it exists */
if (wrapping_auxdata->orig_auxdata != NULL) {
NPY_AUXDATA_FREE(wrapping_auxdata->orig_auxdata);
wrapping_auxdata->orig_auxdata = NULL;
}
/* Reset descriptors pointers to NULL */
for (int i = 0; i < NPY_MAXARGS; i++) {
wrapping_auxdata->descriptors[i] = NULL;
}
/* If there is space in the freelist, store this instance for reuse */
if (wrapping_auxdata_freenum < WRAPPING_AUXDATA_FREELIST_SIZE) {
wrapping_auxdata_freelist[wrapping_auxdata_freenum++] = wrapping_auxdata;
} else {
/* Otherwise, free the memory directly */
PyMem_FREE(wrapping_auxdata);
}
}
注释:
# 释放 wrapping_auxdata 结构体中 orig_auxdata 指针指向的内存块
NPY_AUXDATA_FREE(wrapping_auxdata->orig_auxdata);
# 将 wrapping_auxdata 结构体的 orig_auxdata 指针置为 NULL,防止悬空引用
wrapping_auxdata->orig_auxdata = NULL;
# 检查 wrapping_auxdata_freelist 是否还有空间存储 wrapping_auxdata
if (wrapping_auxdata_freenum < WRAPPING_AUXDATA_FREELIST_SIZE) {
# 将 wrapping_auxdata 添加到 wrapping_auxdata_freelist 中
wrapping_auxdata_freelist[wrapping_auxdata_freenum] = wrapping_auxdata;
# 更新 wrapping_auxdata_freenum 指示下一个可用位置
wrapping_auxdata_freenum++;
}
else {
# 如果 wrapping_auxdata_freelist 已满,则释放 wrapping_auxdata 占用的内存
PyMem_Free(wrapping_auxdata);
}
}
/*
* 获取可重用的包装辅助数据结构
* 如果有空闲的辅助数据结构可用,则返回一个;否则分配一个新的
*/
static wrapping_auxdata *
get_wrapping_auxdata(void)
{
wrapping_auxdata *res;
if (wrapping_auxdata_freenum > 0) {
// 如果有空闲的辅助数据结构可用,则从空闲列表中取出一个
wrapping_auxdata_freenum--;
res = wrapping_auxdata_freelist[wrapping_auxdata_freenum];
}
else {
// 否则,分配一个新的辅助数据结构
res = PyMem_Calloc(1, sizeof(wrapping_auxdata));
if (res < 0) {
// 内存分配失败时,设置错误状态并返回空指针
PyErr_NoMemory();
return NULL;
}
// 设置辅助数据结构的释放函数
res->base.free = (void *)wrapping_auxdata_free;
// 将原始上下文的描述符设置为辅助数据结构的描述符数组
res->orig_context.descriptors = res->descriptors;
}
return res;
}
/*
* 包装方法的分块循环函数
* 如果上下文中有更多的东西被存储,可能需要在这里复制它们。但当前情况下不需要。
*/
static int
wrapping_method_strided_loop(PyArrayMethod_Context *NPY_UNUSED(context),
char *const data[], npy_intp const dimensions[],
npy_intp const strides[], wrapping_auxdata *auxdata)
{
return auxdata->orig_loop(
&auxdata->orig_context, data, dimensions, strides,
auxdata->orig_auxdata);
}
/*
* 获取包装方法的循环函数
* 设置包装方法的循环函数及其传输数据
*/
static int
wrapping_method_get_loop(
PyArrayMethod_Context *context,
int aligned, int move_references, const npy_intp *strides,
PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata,
NPY_ARRAYMETHOD_FLAGS *flags)
{
assert(move_references == 0); /* 仅在内部用于“decref”函数 */
int nin = context->method->nin, nout = context->method->nout;
// 获取可重用的包装辅助数据结构
wrapping_auxdata *auxdata = get_wrapping_auxdata();
if (auxdata == NULL) {
return -1;
}
// 设置原始上下文的方法为被包装的方法
auxdata->orig_context.method = context->method->wrapped_meth;
auxdata->orig_context.caller = context->caller;
// 将描述符从被包装方法的格式转换回原始格式
if (context->method->translate_given_descrs(
nin, nout, context->method->wrapped_dtypes, context->descriptors,
(PyArray_Descr **)auxdata->orig_context.descriptors) < 0) {
NPY_AUXDATA_FREE((NpyAuxData *)auxdata);
return -1;
}
// 获取被包装方法的分块循环
if (context->method->wrapped_meth->get_strided_loop(
&auxdata->orig_context, aligned, 0, strides,
&auxdata->orig_loop, &auxdata->orig_auxdata,
flags) < 0) {
NPY_AUXDATA_FREE((NpyAuxData *)auxdata);
return -1;
}
// 返回包装方法的分块循环及其传输数据
*out_loop = (PyArrayMethod_StridedLoop *)&wrapping_method_strided_loop;
*out_transferdata = (NpyAuxData *)auxdata;
return 0;
}
/*
* 包装原始身份函数,需要将描述符翻译回原始的描述符,并提供一个“原始”上下文(与get_loop完全相同)
* 我们再次假设描述符翻译是快速的。
*/
static int
wrapping_method_get_identity_function(
PyArrayMethod_Context *context, npy_bool reduction_is_empty,
char *item)
{
/* 复制上下文,并替换描述符 */
PyArrayMethod_Context orig_context = *context;
PyArray_Descr *orig_descrs[NPY_MAXARGS];
orig_context.descriptors = orig_descrs;
orig_context.method = context->method->wrapped_meth;
// 获取输入和输出参数的数量
int nin = context->method->nin, nout = context->method->nout;
// 获取数据类型元信息数组的指针
PyArray_DTypeMeta **dtypes = context->method->wrapped_dtypes;
// 调用方法对象的 translate_given_descrs 函数,将描述符转换为原始描述符
if (context->method->translate_given_descrs(
nin, nout, dtypes, context->descriptors, orig_descrs) < 0) {
// 如果转换失败,返回 -1
return -1;
}
// 调用方法对象的 wrapped_meth 成员的 get_reduction_initial 函数,获取初始值
int res = context->method->wrapped_meth->get_reduction_initial(
&orig_context, reduction_is_empty, item);
// 循环释放原始描述符的引用计数
for (int i = 0; i < nin + nout; i++) {
Py_DECREF(orig_descrs);
}
// 返回初始值获取的结果
return res;
/*UFUNC_API
* 允许在现有的ufunc循环周围创建一个相当轻量级的包装器。
* 主要用于单位,因为它目前有些限制,即强制您不能使用另一个ufunc的循环。
*
* @param ufunc_obj
* @param new_dtypes
* @param wrapped_dtypes
* @param translate_given_descrs 参见typedef注释
* @param translate_loop_descrs 参见typedef注释
* @return 成功返回0,失败返回-1
*/
NPY_NO_EXPORT int
PyUFunc_AddWrappingLoop(PyObject *ufunc_obj,
PyArray_DTypeMeta *new_dtypes[], PyArray_DTypeMeta *wrapped_dtypes[],
PyArrayMethod_TranslateGivenDescriptors *translate_given_descrs,
PyArrayMethod_TranslateLoopDescriptors *translate_loop_descrs)
{
int res = -1;
PyUFuncObject *ufunc = (PyUFuncObject *)ufunc_obj;
PyObject *wrapped_dt_tuple = NULL;
PyObject *new_dt_tuple = NULL;
PyArrayMethodObject *meth = NULL;
if (!PyObject_TypeCheck(ufunc_obj, &PyUFunc_Type)) {
PyErr_SetString(PyExc_TypeError,
"ufunc object passed is not a ufunc!");
return -1;
}
wrapped_dt_tuple = PyArray_TupleFromItems(
ufunc->nargs, (PyObject **)wrapped_dtypes, 1);
if (wrapped_dt_tuple == NULL) {
goto finish;
}
PyArrayMethodObject *wrapped_meth = NULL;
PyObject *loops = ufunc->_loops;
Py_ssize_t length = PyList_Size(loops);
for (Py_ssize_t i = 0; i < length; i++) {
PyObject *item = PyList_GetItemRef(loops, i);
PyObject *cur_DType_tuple = PyTuple_GetItem(item, 0);
Py_DECREF(item);
int cmp = PyObject_RichCompareBool(cur_DType_tuple, wrapped_dt_tuple, Py_EQ);
if (cmp < 0) {
goto finish;
}
if (cmp == 0) {
continue;
}
wrapped_meth = (PyArrayMethodObject *)PyTuple_GET_ITEM(item, 1);
if (!PyObject_TypeCheck(wrapped_meth, &PyArrayMethod_Type)) {
PyErr_SetString(PyExc_TypeError,
"Matching loop was not an ArrayMethod.");
goto finish;
}
break;
}
if (wrapped_meth == NULL) {
PyErr_Format(PyExc_TypeError,
"Did not find the to-be-wrapped loop in the ufunc with given "
"DTypes. Received wrapping types: %S", wrapped_dt_tuple);
goto finish;
}
PyType_Slot slots[] = {
{NPY_METH_resolve_descriptors, &wrapping_method_resolve_descriptors},
{NPY_METH_get_loop, &wrapping_method_get_loop},
{NPY_METH_get_reduction_initial,
&wrapping_method_get_identity_function},
{0, NULL}
};
PyArrayMethod_Spec spec = {
.name = "wrapped-method",
.nin = wrapped_meth->nin,
.nout = wrapped_meth->nout,
.casting = wrapped_meth->casting,
.flags = wrapped_meth->flags,
.dtypes = new_dtypes,
.slots = slots,
};
PyBoundArrayMethodObject *bmeth = PyArrayMethod_FromSpec_int(&spec, 1);
# 如果 bmeth 为 NULL,则跳转到 finish 标签处,结束函数
if (bmeth == NULL) {
goto finish;
}
# 增加 bmeth->method 的引用计数,并将其赋值给 meth
Py_INCREF(bmeth->method);
meth = bmeth->method;
# 将 bmeth 置为 NULL
Py_SETREF(bmeth, NULL);
/* 完成新 ArrayMethod 的“包装”部分 */
# 分配内存以存储 wrapped_dtypes 数组,大小为 ufunc->nargs 个 PyArray_DTypeMeta* 元素的空间
meth->wrapped_dtypes = PyMem_Malloc(ufunc->nargs * sizeof(PyArray_DTypeMeta *));
if (meth->wrapped_dtypes == NULL) {
goto finish;
}
# 增加 wrapped_meth 的引用计数,并将其赋值给 meth->wrapped_meth
Py_INCREF(wrapped_meth);
meth->wrapped_meth = wrapped_meth;
# 设置 meth 的 translate_given_descrs 和 translate_loop_descrs 属性
meth->translate_given_descrs = translate_given_descrs;
meth->translate_loop_descrs = translate_loop_descrs;
# 复制 wrapped_dtypes 数组的每个元素到 meth->wrapped_dtypes 数组中
for (int i = 0; i < ufunc->nargs; i++) {
Py_XINCREF(wrapped_dtypes[i]);
meth->wrapped_dtypes[i] = wrapped_dtypes[i];
}
# 从 new_dtypes 中创建一个包含 ufunc->nargs 个元素的元组 new_dt_tuple
new_dt_tuple = PyArray_TupleFromItems(
ufunc->nargs, (PyObject **)new_dtypes, 1);
# 如果创建元组失败,则跳转到 finish 标签处,结束函数
if (new_dt_tuple == NULL) {
goto finish;
}
# 创建一个元组 info,包含 new_dt_tuple 和 meth 两个元素
PyObject *info = PyTuple_Pack(2, new_dt_tuple, meth);
# 如果创建元组 info 失败,则跳转到 finish 标签处,结束函数
if (info == NULL) {
goto finish;
}
# 将 info 作为参数调用 PyUFunc_AddLoop 函数,并将结果赋值给 res
res = PyUFunc_AddLoop(ufunc, info, 0);
# 减少 info 的引用计数
Py_DECREF(info);
finish:
# 递减并释放 wrapped_dt_tuple 的引用计数
Py_XDECREF(wrapped_dt_tuple);
# 递减并释放 new_dt_tuple 的引用计数
Py_XDECREF(new_dt_tuple);
# 递减并释放 meth 的引用计数
Py_XDECREF(meth);
# 返回 res 变量
return res;
}
# 这行代码是一个单独的右花括号 '}',用于结束一个代码块或数据结构的定义。
# 在很多编程语言中,花括号用于界定代码块的范围,例如函数、循环、条件语句等。
# 在这段代码中,它是代码块的结尾,可能是一个函数、类、循环或条件语句的末尾。
.\numpy\numpy\_core\src\umath\_operand_flag_tests.c
static PyMethodDef TestMethods[] = {
{NULL, NULL, 0, NULL}
};
static void
inplace_add(char **args, npy_intp const *dimensions, npy_intp const *steps, void *data)
{
npy_intp i;
npy_intp n = dimensions[0];
char *in1 = args[0]; // 指向第一个输入数组的指针
char *in2 = args[1]; // 指向第二个输入数组的指针
npy_intp in1_step = steps[0]; // 第一个输入数组的步长
npy_intp in2_step = steps[1]; // 第二个输入数组的步长
for (i = 0; i < n; i++) {
(*(npy_intp *)in1) = *(npy_intp*)in1 + *(npy_intp*)in2; // 将第二个数组的值加到第一个数组中
in1 += in1_step; // 更新第一个数组的指针位置
in2 += in2_step; // 更新第二个数组的指针位置
}
}
/*This a pointer to the above function*/
PyUFuncGenericFunction funcs[1] = {&inplace_add};
/* These are the input and return dtypes of logit.*/
static const char types[2] = {NPY_INTP, NPY_INTP}; // 输入和返回的数据类型为整型
static void *const data[1] = {NULL};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_operand_flag_tests", // 模块名
NULL, // 模块文档字符串
-1, // 模块状态,-1表示使用默认状态
TestMethods, // 模块的方法定义
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC PyInit__operand_flag_tests(void)
{
PyObject *m = NULL;
PyObject *ufunc;
m = PyModule_Create(&moduledef); // 创建Python模块对象
if (m == NULL) {
goto fail;
}
import_array(); // 导入NumPy的数组对象API
import_umath(); // 导入NumPy的数学函数API
// 创建并初始化ufunc对象,用于执行inplace_add函数
ufunc = PyUFunc_FromFuncAndData(funcs, data, types, 1, 2, 0,
PyUFunc_None, "inplace_add",
"inplace_add_docstring", 0);
/*
* Set flags to turn off buffering for first input operand,
* so that result can be written back to input operand.
*/
// 设置操作标志,以关闭第一个输入操作数的缓冲区,从而可以将结果写回到输入操作数中
((PyUFuncObject*)ufunc)->op_flags[0] = NPY_ITER_READWRITE;
((PyUFuncObject*)ufunc)->iter_flags = NPY_ITER_REDUCE_OK;
PyModule_AddObject(m, "inplace_add", (PyObject*)ufunc); // 将ufunc对象添加到模块中
return m;
fail:
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError,
"cannot load _operand_flag_tests module.");
}
if (m) {
Py_DECREF(m);
m = NULL;
}
return m;
}
.\numpy\numpy\_core\src\umath\_rational_tests.c
/* Fixed size rational numbers exposed to Python */
/* 包含必要的头文件:Python.h 和 structmember.h */
/* 定义 NPY_NO_DEPRECATED_API,使用最新的 NumPy API 版本 */
/* 包含 math.h 头文件 */
/* Relevant arithmetic exceptions */
/* Uncomment the following line to work around a bug in numpy */
/*
/* 定义设置溢出异常的函数 */
static void
set_overflow(void) {
/* 如果定义了 ACQUIRE_GIL,则获取全局解释器锁 */
PyGILState_STATE state = PyGILState_Ensure();
/* 如果没有发生异常,则设置 OverflowError 异常 */
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_OverflowError,
"overflow in rational arithmetic");
}
/* 释放全局解释器锁 */
PyGILState_Release(state);
}
/* 定义设置零除异常的函数 */
static void
set_zero_divide(void) {
/* 如果定义了 ACQUIRE_GIL,则获取全局解释器锁 */
PyGILState_STATE state = PyGILState_Ensure();
/* 如果没有发生异常,则设置 ZeroDivisionError 异常 */
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_ZeroDivisionError,
"zero divide in rational arithmetic");
}
/* 释放全局解释器锁 */
PyGILState_Release(state);
}
/* Integer arithmetic utilities */
/* 定义安全的取负数函数 */
static inline npy_int32
safe_neg(npy_int32 x) {
/* 如果 x 是最小的负数,设置溢出异常 */
if (x==(npy_int32)1<<31) {
set_overflow();
}
return -x;
}
/* 定义安全的取绝对值函数(32位) */
static inline npy_int32
safe_abs32(npy_int32 x) {
npy_int32 nx;
/* 如果 x 大于等于 0,直接返回 x */
if (x>=0) {
return x;
}
/* 否则计算绝对值,如果溢出则设置异常 */
nx = -x;
if (nx<0) {
set_overflow();
}
return nx;
}
/* 定义安全的取绝对值函数(64位) */
static inline npy_int64
safe_abs64(npy_int64 x) {
npy_int64 nx;
/* 如果 x 大于等于 0,直接返回 x */
if (x>=0) {
return x;
}
/* 否则计算绝对值,如果溢出则设置异常 */
nx = -x;
if (nx<0) {
set_overflow();
}
return nx;
}
/* 计算最大公约数函数 */
static inline npy_int64
gcd(npy_int64 x, npy_int64 y) {
/* 取两数的绝对值 */
x = safe_abs64(x);
y = safe_abs64(y);
/* 辗转相除法计算最大公约数 */
if (x < y) {
npy_int64 t = x;
x = y;
y = t;
}
while (y) {
npy_int64 t;
x = x%y;
t = x;
x = y;
y = t;
}
return x;
}
/* 计算最小公倍数函数 */
static inline npy_int64
lcm(npy_int64 x, npy_int64 y) {
npy_int64 lcm;
/* 如果 x 或 y 为 0,直接返回 0 */
if (!x || !y) {
return 0;
}
/* 计算最小公倍数,并检查是否溢出 */
x /= gcd(x,y);
lcm = x*y;
if (lcm/y!=x) {
set_overflow();
}
return safe_abs64(lcm);
}
/* Fixed precision rational numbers */
/* 定义有理数结构体 */
typedef struct {
/* 分子 */
npy_int32 n;
/*
* 分母减一: numpy.zeros() 为非对象类型使用 memset(0),确保 rational(0) 全为零字节
*/
npy_int32 dmm;
} rational;
/* 定义创建整数有理数的函数 */
static inline rational
make_rational_int(npy_int64 n) {
rational r = {(npy_int32)n,0};
/* 如果分子不等于原整数 n,设置溢出异常 */
if (r.n != n) {
set_overflow();
}
return r;
}
/* 定义创建有理数的函数(慢速版本) */
static rational
make_rational_slow(npy_int64 n_, npy_int64 d_) {
rational r = {0};
/* 如果分母为零,设置零除异常 */
if (!d_) {
set_zero_divide();
}
/* 省略部分代码,未完整给出 */
}
// 否则分支:处理分数的规范化
else {
// 计算分子和分母的最大公约数
npy_int64 g = gcd(n_, d_);
// 声明整型变量 d
npy_int32 d;
// 简化分数:分子除以最大公约数,分母除以最大公约数
n_ /= g;
d_ /= g;
// 将简化后的分子转换为 npy_int32 类型,并赋给结果结构体的分子成员
r.n = (npy_int32)n_;
// 将简化后的分母赋给变量 d
d = (npy_int32)d_;
// 如果简化后的分子或分母与原始值不相等,设置溢出标志
if (r.n != n_ || d != d_) {
set_overflow();
}
// 否则,处理正常情况
else {
// 如果分母小于等于 0,取其绝对值并将结果结构体的分子成员取反
if (d <= 0) {
d = -d;
r.n = safe_neg(r.n);
}
// 将分母减 1 的结果赋给结果结构体的 dmm 成员
r.dmm = d - 1;
}
}
// 返回处理后的结果结构体
return r;
}
static inline npy_int32
d(rational r) {
return r.dmm+1;
}
/* Assumes d_ > 0 */
// 快速创建有理数的函数,假设分母大于0
static rational
make_rational_fast(npy_int64 n_, npy_int64 d_) {
// 计算最大公约数
npy_int64 g = gcd(n_,d_);
rational r;
// 简化分数
n_ /= g;
d_ /= g;
r.n = (npy_int32)n_;
r.dmm = (npy_int32)(d_-1);
// 如果简化后分数不等于原始分数,则设置溢出标志
if (r.n!=n_ || r.dmm+1!=d_) {
set_overflow();
}
return r;
}
static inline rational
rational_negative(rational r) {
rational x;
// 对有理数取负
x.n = safe_neg(r.n);
x.dmm = r.dmm;
return x;
}
static inline rational
rational_add(rational x, rational y) {
/*
* Note that the numerator computation can never overflow int128_t,
* since each term is strictly under 2**128/4 (since d > 0).
*/
// 有理数加法,注意分子计算不会溢出,因为每项严格小于2**128/4
return make_rational_fast((npy_int64)x.n*d(y)+(npy_int64)d(x)*y.n,
(npy_int64)d(x)*d(y));
}
static inline rational
rational_subtract(rational x, rational y) {
/* We're safe from overflow as with + */
// 有理数减法,与加法类似,不会溢出
return make_rational_fast((npy_int64)x.n*d(y)-(npy_int64)d(x)*y.n,
(npy_int64)d(x)*d(y));
}
static inline rational
rational_multiply(rational x, rational y) {
/* We're safe from overflow as with + */
// 有理数乘法,与加法类似,不会溢出
return make_rational_fast((npy_int64)x.n*y.n,(npy_int64)d(x)*d(y));
}
static inline rational
rational_divide(rational x, rational y) {
// 有理数除法,使用慢速的方式进行计算
return make_rational_slow((npy_int64)x.n*d(y),(npy_int64)d(x)*y.n);
}
static inline npy_int64
rational_floor(rational x) {
/* Always round down */
// 有理数向下取整
if (x.n>=0) {
return x.n/d(x);
}
/*
* This can be done without casting up to 64 bits, but it requires
* working out all the sign cases
*/
// 处理负数情况的向下取整
return -((-(npy_int64)x.n+d(x)-1)/d(x));
}
static inline npy_int64
rational_ceil(rational x) {
// 有理数向上取整
return -rational_floor(rational_negative(x));
}
static inline rational
rational_remainder(rational x, rational y) {
// 计算有理数的余数
return rational_subtract(x, rational_multiply(y,make_rational_int(
rational_floor(rational_divide(x,y)))));
}
static inline rational
rational_abs(rational x) {
rational y;
// 计算有理数的绝对值
y.n = safe_abs32(x.n);
y.dmm = x.dmm;
return y;
}
static inline npy_int64
rational_rint(rational x) {
/*
* Round towards nearest integer, moving exact half integers towards
* zero
*/
// 四舍五入到最近的整数,对半整数向零舍入
npy_int32 d_ = d(x);
return (2*(npy_int64)x.n+(x.n<0?-d_:d_))/(2*(npy_int64)d_);
}
static inline int
rational_sign(rational x) {
// 返回有理数的符号
return x.n<0?-1:x.n==0?0:1;
}
static inline rational
rational_inverse(rational x) {
rational y = {0};
if (!x.n) {
// 如果有理数为零,则设置零除错误
set_zero_divide();
}
else {
npy_int32 d_;
y.n = d(x);
d_ = x.n;
if (d_ <= 0) {
d_ = safe_neg(d_);
y.n = -y.n;
}
y.dmm = d_-1;
}
return y;
}
static inline int
rational_eq(rational x, rational y) {
/*
* Since we enforce d > 0, and store fractions in reduced form,
* equality is easy.
*/
// 判断两个有理数是否相等,由于我们要求 d > 0 并且以最简分数形式存储,因此判断相等很简单
return x.n==y.n && x.dmm==y.dmm;
}
static inline int
static int
scan_rational(const char** s, rational* x) {
// 定义变量以保存分子、分母及偏移量
long n,d;
int offset;
// 定义指针 ss,指向字符串起始位置
const char* ss;
// 尝试从字符串 *s 中读取长整型数值 n,同时记录偏移量到 offset
if (sscanf(*s,"%ld%n",&n,&offset)<=0) {
// 若未成功读取,返回 0 表示失败
return 0;
}
// 更新 ss 为 *s 加上偏移量 offset 的位置
ss = *s+offset;
// 如果 ss 指向的字符不是 '/',说明只有分子,构造一个整数有理数并返回 1
if (*ss!='/') {
*s = ss;
*x = make_rational_int(n);
return 1;
}
// 否则,ss 指向 '/',接着解析分母 d
ss++;
// 尝试从 ss 中读取长整型数值 d,同时记录偏移量到 offset
if (sscanf(ss,"%ld%n",&d,&offset)<=0 || d<=0) {
// 如果未能成功读取或分母 d 小于等于 0,返回 0 表示失败
return 0;
}
// 更新 *s 为 ss 加上偏移量 offset 的位置
*s = ss+offset;
// 使用分子 n 和分母 d 构造一个有理数并保存到 *x
*x = make_rational_slow(n,d);
// 返回 1 表示成功解析有理数
return 1;
}
这段代码主要用于解析字符串表示的有理数,包括处理只有分子或分子分母都有的情况,返回对应的有理数对象或者指示解析失败。
// 循环遍历参数元组中的每个元素,i 是循环变量,从 0 开始到 size-1
for (i=0; i<size; i++) {
PyObject* y;
int eq;
// 将参数元组中第 i 个元素赋值给 x[i]
x[i] = PyTuple_GET_ITEM(args, i);
// 将 x[i] 转换为长整型并赋值给 n[i]
n[i] = PyLong_AsLong(x[i]);
// 检查是否在转换过程中发生错误
if (error_converting(n[i])) {
// 如果是类型错误,则设置异常并返回 0
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError,
"expected integer %s, got %s",
// 根据 i 的值选择错误信息中的描述
(i ? "denominator" : "numerator"),
// 获取参数类型的名称
x[i]->ob_type->tp_name);
}
return 0;
}
/* 检查是否为精确整数 */
// 创建一个长整型对象 y,用于比较
y = PyLong_FromLong(n[i]);
// 如果创建对象失败,则返回 0
if (!y) {
return 0;
}
// 使用 RichCompare 检查 x[i] 和 y 是否相等
eq = PyObject_RichCompareBool(x[i],y,Py_EQ);
// 释放对象 y
Py_DECREF(y);
// 如果比较出错,则返回 0
if (eq<0) {
return 0;
}
// 如果不相等,则设置类型错误异常并返回 0
if (!eq) {
PyErr_Format(PyExc_TypeError,
"expected integer %s, got %s",
// 根据 i 的值选择错误信息中的描述
(i ? "denominator" : "numerator"),
// 获取参数类型的名称
x[i]->ob_type->tp_name);
return 0;
}
}
// 调用 make_rational_slow 函数生成一个有理数对象 r
r = make_rational_slow(n[0],n[1]);
// 如果在生成有理数对象过程中发生异常,则返回 0
if (PyErr_Occurred()) {
return 0;
}
// 返回 Python 中的有理数对象 PyRational
return PyRational_FromRational(r);
/*
* 宏定义 AS_RATIONAL(dst,object) 将对象转换为 rational 结构体,赋给 dst。
* 如果对象是 PyRational 类型,则直接获取其值。
* 如果对象是整数类型,尝试将其转换为 long 型,然后生成对应的 PyLong 对象进行比较和处理。
* 若转换失败,根据情况返回 Py_NotImplemented 或 0,或者抛出异常。
*/
{ \
dst.n = 0; \ // 初始化 dst 结构体的分子部分为 0
if (PyRational_Check(object)) { \ // 检查 object 是否为 PyRational 类型
dst = ((PyRational*)object)->r; \ // 若是,直接获取其 rational 结构体赋给 dst
} \
else { \ // 如果不是 PyRational 类型
PyObject* y_; \ // 定义一个 PyObject 指针 y_
int eq_; \ // 定义一个整型变量 eq_
long n_ = PyLong_AsLong(object); \ // 尝试将 object 转换为 long 型
if (error_converting(n_)) { \ // 如果转换失败
if (PyErr_ExceptionMatches(PyExc_TypeError)) { \ // 如果是类型错误异常
PyErr_Clear(); \ // 清除异常状态
Py_INCREF(Py_NotImplemented); \ // 增加 Py_NotImplemented 的引用计数
return Py_NotImplemented; \ // 返回 Py_NotImplemented 对象
} \
return 0; \ // 转换出错,返回 0 表示失败
} \
y_ = PyLong_FromLong(n_); \ // 将 n_ 转换为 PyLong 对象 y_
if (!y_) { \ // 如果转换失败(y_ 为 NULL)
return 0; \ // 返回 0 表示失败
} \
eq_ = PyObject_RichCompareBool(object,y_,Py_EQ); \ // 使用 PyObject_RichCompareBool 比较 object 和 y_ 是否相等
Py_DECREF(y_); \ // 减少 PyLong 对象 y_ 的引用计数
if (eq_<0) { \ // 如果比较出错
return 0; \ // 返回 0 表示失败
} \
if (!eq_) { \ // 如果不相等
Py_INCREF(Py_NotImplemented); \ // 增加 Py_NotImplemented 的引用计数
return Py_NotImplemented; \ // 返回 Py_NotImplemented 对象
} \
dst = make_rational_int(n_); \ // 将 n_ 转换为 rational 结构体赋给 dst
} \
}
/*
* 定义了一个函数 pyrational_richcompare,用于比较两个 PyRational 对象的大小关系。
* 根据传入的操作符 op,调用不同的比较函数进行比较。
* 返回一个 PyBool 对象表示比较结果。
*/
static PyObject*
pyrational_richcompare(PyObject* a, PyObject* b, int op) {
rational x, y; \ // 定义 rational 结构体变量 x 和 y
int result = 0; \ // 定义整型变量 result 并初始化为 0
AS_RATIONAL(x,a); \ // 将 a 转换为 rational 结构体赋给 x
AS_RATIONAL(y,b); \ // 将 b 转换为 rational 结构体赋给 y
switch (op) { \ // 根据操作符 op 执行不同的比较
OP(Py_LT,lt) \ // 小于操作
OP(Py_LE,le) \ // 小于等于操作
OP(Py_EQ,eq) \ // 等于操作
OP(Py_NE,ne) \ // 不等于操作
OP(Py_GT,gt) \ // 大于操作
OP(Py_GE,ge) \ // 大于等于操作
}; \ // 结束 switch
return PyBool_FromLong(result); \ // 返回比较结果的 PyBool 对象
}
/*
* 定义了一个函数 pyrational_repr,用于返回 PyRational 对象的字符串表示形式。
* 如果 rational 结构体 x 的分母不为 1,则返回形如 "rational(分子,分母)" 的字符串。
* 否则,返回形如 "rational(分子)" 的字符串。
*/
static PyObject*
pyrational_repr(PyObject* self) {
rational x = ((PyRational*)self)->r; \ // 将 self 转换为 PyRational 对象,并获取其 rational 结构体赋给 x
if (d(x)!=1) { \ // 如果 x 的分母不为 1
return PyUnicode_FromFormat(
"rational(%ld,%ld)",(long)x.n,(long)d(x)); \ // 返回带分子和分母的格式化字符串对象
}
else { \ // 如果 x 的分母为 1
return PyUnicode_FromFormat(
"rational(%ld)",(long)x.n); \ // 返回只带分子的格式化字符串对象
}
}
/*
* 定义了一个函数 pyrational_str,用于返回 PyRational 对象的简化字符串表示形式。
* 如果 rational 结构体 x 的分母不为 1,则返回形如 "分子/分母" 的字符串。
* 否则,返回形如 "分子" 的字符串。
*/
static PyObject*
pyrational_str(PyObject* self) {
rational x = ((PyRational*)self)->r; \ // 将 self 转换为 PyRational 对象,并获取其 rational 结构体赋给 x
if (d(x)!=1) { \ // 如果 x 的分母不为 1
return PyUnicode_FromFormat(
"%ld/%ld",(long)x.n,(long)d(x)); \ // 返回带分子和分母的格式化字符串对象
}
else { \ // 如果 x 的分母为 1
return PyUnicode_FromFormat(
"%ld",(long)x.n); \ // 返回只带分子的格式化字符串对象
}
}
/*
* 定义了一个函数 pyrational_hash,用于计算 PyRational 对象的哈希值。
* 使用较弱的哈希算法,计算结果为 h。
* 如果计算结果为 -1,则返回特定的哈希值 2,以避免返回 -1。
*/
static npy_hash_t
pyrational_hash(PyObject* self) {
rational x = ((PyRational*)self)->r; \ // 将 self 转换为 PyRational 对象,并获取其 rational 结构体赋给 x
/* 使用 Python 期望的较弱哈希算法 */
long h = 131071*x.n+524287*x.dmm; \ // 计算哈希值 h
/* 不返回特定的错误值 -1 */
return h==-1?2:h; \ // 如果 h 为 -1,则返回 2;否则返回 h 本身
}
/*
* 宏定义 RATIONAL_BINOP_2(name,exp) 定义了一组二元操作函数。
* 对于给定的操作名 name 和表达式 exp,定义了一个函数 pyrational_
* 该函数接受两个 PyObject 对象 a 和 b,将它们转换为 rational 结构体 x 和 y,然后计算表达式 exp 得到 z。
* 如果在计算过程中出现错误,则返回 0 表示失败;否则返回 z 的 PyRational 表示。
*/
static PyObject* \
pyrational_
rational x, y, z; \ // 定义 rational 结构体变量 x, y 和 z
AS_RATIONAL(x,a); \ // 将 a 转换为 rational 结构体赋给 x
AS_RATIONAL(y,b); \ // 将 b 转换为 rational 结构体赋给 y
z = exp; \ // 计算 exp 并将
make_rational_int(rational_floor(rational_divide(x,y))))
static PyObject* \
pyrational_
rational x = ((PyRational*)self)->r; \
type y = exp; \
// 检查是否有异常发生
if (PyErr_Occurred()) { \
return 0; \
} \
// 转换并返回结果
return convert(y); \
}
// 定义名为 negative 的一元运算函数
RATIONAL_UNOP(negative,rational,rational_negative(x),PyRational_FromRational)
// 定义名为 absolute 的一元运算函数
RATIONAL_UNOP(absolute,rational,rational_abs(x),PyRational_FromRational)
// 定义名为 int 的一元运算函数
RATIONAL_UNOP(int,long,rational_int(x),PyLong_FromLong)
// 定义名为 float 的一元运算函数
RATIONAL_UNOP(float,double,rational_double(x),PyFloat_FromDouble)
// 定义名为 positive 的一元运算函数
static PyObject*
pyrational_positive(PyObject* self) {
// 增加对象的引用计数并返回对象本身
Py_INCREF(self);
return self;
}
// 定义名为 nonzero 的一元运算函数
static int
pyrational_nonzero(PyObject* self) {
// 获取对象中的 rational 结构体并检查是否非零
rational x = ((PyRational*)self)->r;
return rational_nonzero(x);
}
// 定义 PyNumberMethods 结构体及其成员函数指针
static PyNumberMethods pyrational_as_number = {
pyrational_add, /* nb_add */
pyrational_subtract, /* nb_subtract */
pyrational_multiply, /* nb_multiply */
pyrational_remainder, /* nb_remainder */
0, /* nb_divmod */
0, /* nb_power */
pyrational_negative, /* nb_negative */
pyrational_positive, /* nb_positive */
pyrational_absolute, /* nb_absolute */
pyrational_nonzero, /* nb_nonzero */
0, /* nb_invert */
0, /* nb_lshift */
0, /* nb_rshift */
0, /* nb_and */
0, /* nb_xor */
0, /* nb_or */
pyrational_int, /* nb_int */
0, /* reserved */
pyrational_float, /* nb_float */
0, /* nb_inplace_add */
0, /* nb_inplace_subtract */
0, /* nb_inplace_multiply */
0, /* nb_inplace_remainder */
0, /* nb_inplace_power */
0, /* nb_inplace_lshift */
0, /* nb_inplace_rshift */
0, /* nb_inplace_and */
0, /* nb_inplace_xor */
0, /* nb_inplace_or */
pyrational_floor_divide, /* nb_floor_divide */
pyrational_divide, /* nb_true_divide */
0, /* nb_inplace_floor_divide */
0, /* nb_inplace_true_divide */
0, /* nb_index */
};
// 定义返回 numerator 的函数
static PyObject*
pyrational_n(PyObject* self, void* closure) {
return PyLong_FromLong(((PyRational*)self)->r.n);
}
// 定义返回 denominator 的函数
static PyObject*
pyrational_d(PyObject* self, void* closure) {
return PyLong_FromLong(d(((PyRational*)self)->r));
}
// 定义属性的获取函数列表
static PyGetSetDef pyrational_getset[] = {
{(char*)"n",pyrational_n,0,(char*)"numerator",0},
{(char*)"d",pyrational_d,0,(char*)"denominator",0},
{0} /* sentinel */
};
static PyTypeObject PyRational_Type = {
PyVarObject_HEAD_INIT(NULL, 0) /* 初始化基类对象,无基类,初始大小为0 */
"numpy._core._rational_tests.rational", /* 类型对象的名称 */
sizeof(PyRational), /* 类型对象的基本大小 */
0, /* 每个对象的附加大小 */
0, /* 对象释放函数 */
0, /* 对象打印函数 */
0, /* 获取对象属性的方法 */
0, /* 设置对象属性的方法 */
0, /* 保留字段 */
pyrational_repr, /* 对象的字符串表示形式的函数 */
&pyrational_as_number, /* 数字协议的实现 */
0, /* 序列协议的实现 */
0, /* 映射协议的实现 */
pyrational_hash, /* 哈希函数 */
0, /* 调用对象的函数 */
pyrational_str, /* 对象的字符串表示形式的函数 */
0, /* 获取对象属性的函数 */
0, /* 设置对象属性的函数 */
0, /* 缓冲区协议的实现 */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* 类型标志 */
"Fixed precision rational numbers", /* 类型的文档字符串 */
0, /* 遍历对象的函数 */
0, /* 清除对象的函数 */
pyrational_richcompare, /* 对象之间的比较函数 */
0, /* 弱引用列表偏移量 */
0, /* 迭代器协议的实现 */
0, /* 迭代器的下一个元素的函数 */
0, /* 类型的方法 */
0, /* 类型的成员 */
pyrational_getset, /* 获取和设置对象属性的函数集 */
0, /* 基类对象 */
0, /* 类型的字典 */
0, /* 描述符的获取函数 */
0, /* 描述符的设置函数 */
0, /* 字典偏移量 */
0, /* 初始化对象的函数 */
0, /* 分配对象的函数 */
pyrational_new, /* 创建新对象的函数 */
0, /* 释放对象的函数 */
0, /* 垃圾回收标志 */
0, /* 基类元组 */
0, /* 方法解析顺序 */
0, /* 类型缓存 */
0, /* 子类 */
0, /* 弱引用列表 */
0, /* 对象销毁的函数 */
0, /* 版本标签 */
};
/* NumPy support */
static PyObject*
npyrational_getitem(void* data, void* arr) {
// 声明一个有理数变量 r
rational r;
// 将 data 指向的数据复制到 r 中
memcpy(&r,data,sizeof(rational));
// 调用 PyRational_FromRational 函数,将有理数 r 转换为 Python 对象并返回
return PyRational_FromRational(r);
}
static int
npyrational_setitem(PyObject* item, void* data, void* arr) {
// 声明一个有理数变量 r
rational r;
// 如果 item 是 PyRational 类型的对象
if (PyRational_Check(item)) {
// 从 PyRational 对象中获取有理数 r,并赋值给 r
r = ((PyRational*)item)->r;
}
else {
// 将 item 转换为 long long 类型
long long n = PyLong_AsLongLong(item);
PyObject* y;
int eq;
// 如果转换出错,返回 -1
if (error_converting(n)) {
return -1;
}
// 创建一个新的 PyLong 对象 y
y = PyLong_FromLongLong(n);
if (!y) {
return -1;
}
// 比较 item 和 y 是否相等
eq = PyObject_RichCompareBool(item, y, Py_EQ);
Py_DECREF(y);
// 如果比较失败,返回 -1
if (eq<0) {
return -1;
}
// 如果不相等,抛出 TypeError 异常
if (!eq) {
PyErr_Format(PyExc_TypeError,
"expected rational, got %s", item->ob_type->tp_name);
return -1;
}
// 根据整数 n 创建有理数 r
r = make_rational_int(n);
}
// 将有理数 r 复制到 data 指向的内存中
memcpy(data, &r, sizeof(rational));
return 0;
}
static inline void
byteswap(npy_int32* x) {
// 将 x 指向的 npy_int32 类型数据进行字节交换
char* p = (char*)x;
size_t i;
for (i = 0; i < sizeof(*x)/2; i++) {
size_t j = sizeof(*x)-1-i;
char t = p[i];
p[i] = p[j];
p[j] = t;
}
}
static void
npyrational_copyswapn(void* dst_, npy_intp dstride, void* src_,
npy_intp sstride, npy_intp n, int swap, void* arr) {
// 将 dst_ 和 src_ 指向的数据进行复制和交换
char *dst = (char*)dst_, *src = (char*)src_;
npy_intp i;
// 如果 src 为 NULL,则直接返回
if (!src) {
return;
}
// 如果 swap 为真,则进行数据交换
if (swap) {
for (i = 0; i < n; i++) {
// 获取目标地址 dst+dstride*i 处的有理数 r
rational* r = (rational*)(dst+dstride*i);
// 将源地址 src+sstride*i 处的数据复制到 r 中
memcpy(r,src+sstride*i,sizeof(rational));
// 对 r 中的 n 和 dmm 进行字节交换
byteswap(&r->n);
byteswap(&r->dmm);
}
}
// 如果不需要交换,并且步长相等,则直接进行数据复制
else if (dstride == sizeof(rational) && sstride == sizeof(rational)) {
memcpy(dst, src, n*sizeof(rational));
}
else {
// 否则,按照指定的步长复制数据
for (i = 0; i < n; i++) {
memcpy(dst + dstride*i, src + sstride*i, sizeof(rational));
}
}
}
static void
npyrational_copyswap(void* dst, void* src, int swap, void* arr) {
// 将 src 指向的有理数数据复制到 dst 指向的内存中,并进行字节交换
rational* r;
// 如果 src 为 NULL,则直接返回
if (!src) {
return;
}
// 将 src 强制转换为有理数类型,并复制到 dst 指向的内存中
r = (rational*)dst;
memcpy(r,src,sizeof(rational));
// 如果 swap 为真,则对 r 中的 n 和 dmm 进行字节交换
if (swap) {
byteswap(&r->n);
byteswap(&r->dmm);
}
}
static int
npyrational_compare(const void* d0, const void* d1, void* arr) {
// 比较两个有理数对象的大小关系
rational x = *(rational*)d0,
y = *(rational*)d1;
// 如果 x 小于 y,则返回 -1;如果 x 等于 y,则返回 0;否则返回 1
return rational_lt(x,y)?-1:rational_eq(x,y)?0:1;
}
// 定义一个名为 name 的宏,它接受一个操作符 op
static int \
npyrational_
npy_intp* max_ind, void* arr) { \
const rational* data; \
npy_intp best_i; \
rational best_r; \
npy_intp i; \
if (!n) { \
return 0; \
} \
data = (rational*)data_; \
best_i = 0; \
best_r = data[0]; \
for (i = 1; i < n; i++) { \
if (rational_
best_i = i; \
best_r = data[i]; \
} \
} \
*max_ind = best_i; \
return 0; \
}
FIND_EXTREME(argmin,lt)
FIND_EXTREME(argmax,gt)
static void
npyrational_dot(void* ip0_, npy_intp is0, void* ip1_, npy_intp is1,
void* op, npy_intp n, void* arr) {
rational r = {0};
const char *ip0 = (char*)ip0_, *ip1 = (char*)ip1_;
npy_intp i;
for (i = 0; i < n; i++) {
r = rational_add(r,rational_multiply(*(rational*)ip0,*(rational*)ip1));
ip0 += is0;
ip1 += is1;
}
*(rational*)op = r;
}
static npy_bool
npyrational_nonzero(void* data, void* arr) {
rational r;
memcpy(&r,data,sizeof(r));
return rational_nonzero(r)?NPY_TRUE:NPY_FALSE;
}
static int
npyrational_fill(void* data_, npy_intp length, void* arr) {
rational* data = (rational*)data_;
rational delta = rational_subtract(data[1],data[0]);
rational r = data[1];
npy_intp i;
for (i = 2; i < length; i++) {
r = rational_add(r,delta);
data[i] = r;
}
return 0;
}
static int
npyrational_fillwithscalar(void* buffer_, npy_intp length,
void* value, void* arr) {
rational r = *(rational*)value;
rational* buffer = (rational*)buffer_;
npy_intp i;
for (i = 0; i < length; i++) {
buffer[i] = r;
}
return 0;
}
static PyArray_ArrFuncs npyrational_arrfuncs;
typedef struct { char c; rational r; } align_test;
PyArray_DescrProto npyrational_descr_proto = {
PyObject_HEAD_INIT(0)
&PyRational_Type, /* typeobj */
'V', /* kind */
'r', /* type */
'=', /* byteorder */
/*
* For now, we need NPY_NEEDS_PYAPI in order to make numpy detect our
* exceptions. This isn't technically necessary,
* since we're careful about thread safety, and hopefully future
* versions of numpy will recognize that.
*/
NPY_NEEDS_PYAPI | NPY_USE_GETITEM | NPY_USE_SETITEM, /* hasobject */
0, /* type_num */
sizeof(rational), /* elsize */
offsetof(align_test,r), /* alignment */
0, /* subarray */
0, /* fields */
0, /* names */
&npyrational_arrfuncs, /* f */
};
static void \
npycast_
void* fromarr, void* toarr) { \
const From* from = (From*)from_; \
To* to = (To*)to_; \
npy_intp i; \
for (i = 0; i < n; i++) { \
From x = from[i]; \
statement \
to[i] = y; \
} \
}
DEFINE_CAST(npy_int
DEFINE_CAST(rational,npy_int
npy_int
DEFINE_INT_CAST(8)
DEFINE_INT_CAST(16)
DEFINE_INT_CAST(32)
DEFINE_INT_CAST(64)
DEFINE_CAST(rational,float,double y = rational_double(x);)
// 定义二元通用函数的宏,参数包括函数名、输入类型0、输入类型1、输出类型以及表达式
void name(char** args, npy_intp const *dimensions, \
npy_intp const *steps, void* data) { \
// 定义函数,接受字符指针数组args、维度dimensions、步长steps以及数据指针data作为参数
npy_intp is0 = steps[0], is1 = steps[1], \
os = steps[2], n = *dimensions; \
// 定义输入步长is0、is1,输出步长os以及数据点数n
char *i0 = args[0], *i1 = args[1], *o = args[2]; \
// 初始化输入指针i0、i1和输出指针o
int k; \
// 定义循环计数器k
for (k = 0; k < n; k++) { \
// 循环遍历每个数据点
intype0 x = *(intype0*)i0; \
// 将i0解引用为intype0类型,并赋值给x
intype1 y = *(intype1*)i1; \
// 将i1解引用为intype1类型,并赋值给y
*(outtype*)o = exp; \
// 将exp计算结果转换为outtype类型,并存储到o指向的位置
i0 += is0; i1 += is1; o += os; \
// 更新指针,移动到下一个数据点
} \
}
// 定义有理数二元通用函数的宏,参数包括函数名、类型以及表达式
BINARY_UFUNC(rational_ufunc_
// 使用BINARY_UFUNC宏生成有理数版本的二元通用函数
RATIONAL_BINARY_UFUNC(add,rational,rational_add(x,y))
// 定义有理数加法函数
RATIONAL_BINARY_UFUNC(subtract,rational,rational_subtract(x,y))
// 定义有理数减法函数
RATIONAL_BINARY_UFUNC(multiply,rational,rational_multiply(x,y))
// 定义有理数乘法函数
RATIONAL_BINARY_UFUNC(divide,rational,rational_divide(x,y))
// 定义有理数除法函数
RATIONAL_BINARY_UFUNC(remainder,rational,rational_remainder(x,y))
// 定义有理数求余函数
RATIONAL_BINARY_UFUNC(floor_divide,rational,
make_rational_int(rational_floor(rational_divide(x,y))))
// 定义有理数向下整除函数
PyUFuncGenericFunction rational_ufunc_true_divide = rational_ufunc_divide;
// 设置有理数真除函数为有理数除法函数
RATIONAL_BINARY_UFUNC(minimum,rational,rational_lt(x,y)?x:y)
// 定义有理数最小值函数
RATIONAL_BINARY_UFUNC(maximum,rational,rational_lt(x,y)?y:x)
// 定义有理数最大值函数
RATIONAL_BINARY_UFUNC(equal,npy_bool,rational_eq(x,y))
// 定义有理数相等判断函数
RATIONAL_BINARY_UFUNC(not_equal,npy_bool,rational_ne(x,y))
// 定义有理数不相等判断函数
RATIONAL_BINARY_UFUNC(less,npy_bool,rational_lt(x,y))
// 定义有理数小于判断函数
RATIONAL_BINARY_UFUNC(greater,npy_bool,rational_gt(x,y))
// 定义有理数大于判断函数
RATIONAL_BINARY_UFUNC(less_equal,npy_bool,rational_le(x,y))
// 定义有理数小于等于判断函数
RATIONAL_BINARY_UFUNC(greater_equal,npy_bool,rational_ge(x,y))
// 定义有理数大于等于判断函数
BINARY_UFUNC(gcd_ufunc,npy_int64,npy_int64,npy_int64,gcd(x,y))
// 定义最大公约数函数
BINARY_UFUNC(lcm_ufunc,npy_int64,npy_int64,npy_int64,lcm(x,y))
// 定义最小公倍数函数
// 定义一元通用函数的宏,参数包括函数名、类型以及表达式
void rational_ufunc_
npy_intp const *steps, void* data) { \
// 定义函数,接受字符指针数组args、维度dimensions、步长steps以及数据指针data作为参数
npy_intp is = steps[0], os = steps[1], n = *dimensions; \
// 定义输入步长is、输出步长os以及数据点数n
char *i = args[0], *o = args[1]; \
// 初始化输入指针i和输出指针o
int k; \
// 定义循环计数器k
for (k = 0; k < n; k++) { \
// 循环遍历每个数据点
rational x = *(rational*)i; \
// 将i解引用为rational类型,并赋值给x
*(type*)o = exp; \
// 将exp计算结果转换为type类型,并存储到o指向的位置
i += is; o += os; \
// 更新指针,移动到下一个数据点
} \
}
UNARY_UFUNC(negative,rational,rational_negative(x))
// 定义有理数负数函数
UNARY_UFUNC(absolute,rational,rational_abs(x))
// 定义有理数绝对值函数
UNARY_UFUNC(floor,rational,make_rational_int(rational_floor(x)))
// 定义有理数向下取整函数
UNARY_UFUNC(ceil,rational,make_rational_int(rational_ceil(x)))
// 定义有理数向上取整函数
UNARY_UFUNC(trunc,rational,make_rational_int(x.n/d(x)))
// 定义有理数截断函数
UNARY_UFUNC(square,rational,rational_multiply(x,x))
// 定义有理数平方函数
UNARY_UFUNC(rint,rational,make_rational_int(rational_rint(x)))
// 定义有理数四舍五入函数
UNARY_UFUNC(sign,rational,make_rational_int(rational_sign(x)))
// 定义有理数符号函数
UNARY_UFUNC(reciprocal,rational,rational_inverse(x))
// 定义有理数倒数函数
UNARY_UFUNC(numerator,npy_int64,x.n)
// 定义有理数分子函数
/* 定义 UNARY_UFUNC 宏,接受三个参数:denominator、npy_int64 类型、d(x) 参数 */
UNARY_UFUNC(denominator,npy_int64,d(x))
/* 内联函数,用于有理数矩阵相乘操作 */
static inline void
rational_matrix_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps)
{
/* 指向输入和输出数组数据的指针 */
char *ip1 = args[0]; // 第一个输入数组指针
char *ip2 = args[1]; // 第二个输入数组指针
char *op = args[2]; // 输出数组指针
/* 核心维度的长度 */
npy_intp dm = dimensions[0]; // 第一个维度的长度
npy_intp dn = dimensions[1]; // 第二个维度的长度
npy_intp dp = dimensions[2]; // 第三个维度的长度
/* 核心维度的步长 */
npy_intp is1_m = steps[0]; // 第一个输入数组在第一个维度上的步长
npy_intp is1_n = steps[1]; // 第一个输入数组在第二个维度上的步长
npy_intp is2_n = steps[2]; // 第二个输入数组在第二个维度上的步长
npy_intp is2_p = steps[3]; // 第二个输入数组在第三个维度上的步长
npy_intp os_m = steps[4]; // 输出数组在第一个维度上的步长
npy_intp os_p = steps[5]; // 输出数组在第三个维度上的步长
/* 核心维度的计数器 */
npy_intp m, p;
/* 计算每行/列向量对的点积 */
for (m = 0; m < dm; m++) {
for (p = 0; p < dp; p++) {
npyrational_dot(ip1, is1_n, ip2, is2_n, op, dn, NULL);
/* 移动到第二个输入数组和输出数组的下一列 */
ip2 += is2_p;
op += os_p;
}
/* 重置到第二个输入数组和输出数组的第一列 */
ip2 -= is2_p * p;
op -= os_p * p;
/* 移动到第一个输入数组和输出数组的下一行 */
ip1 += is1_m;
op += os_m;
}
}
/* 通用函数,用于有理数矩阵相乘操作,处理多维数组 */
static void
rational_gufunc_matrix_multiply(char **args, npy_intp const *dimensions,
npy_intp const *steps, void *NPY_UNUSED(func))
{
/* 外部维度的计数器 */
npy_intp N_;
/* 扁平化外部维度的长度 */
npy_intp dN = dimensions[0];
/* 扁平化外部维度的步长,用于输入和输出数组 */
npy_intp s0 = steps[0];
npy_intp s1 = steps[1];
npy_intp s2 = steps[2];
/*
* 循环遍历外部维度,在每次循环中对核心维度执行矩阵相乘操作
*/
for (N_ = 0; N_ < dN; N_++, args[0] += s0, args[1] += s1, args[2] += s2) {
rational_matrix_multiply(args, dimensions+1, steps+3);
}
}
/* 测试函数,用于有理数加法操作,处理一维数组 */
static void
rational_ufunc_test_add(char** args, npy_intp const *dimensions,
npy_intp const *steps, void* data) {
npy_intp is0 = steps[0], is1 = steps[1], os = steps[2], n = *dimensions;
char *i0 = args[0], *i1 = args[1], *o = args[2];
int k;
for (k = 0; k < n; k++) {
npy_int64 x = *(npy_int64*)i0;
npy_int64 y = *(npy_int64*)i1;
*(rational*)o = rational_add(make_rational_fast(x, 1),
make_rational_fast(y, 1));
i0 += is0; i1 += is1; o += os;
}
}
/* 测试函数,用于有理数加法操作,处理多维数组 */
static void
rational_ufunc_test_add_rationals(char** args, npy_intp const *dimensions,
npy_intp const *steps, void* data) {
npy_intp is0 = steps[0], is1 = steps[1], os = steps[2], n = *dimensions;
char *i0 = args[0], *i1 = args[1], *o = args[2];
int k;
/* 循环遍历多维数组,对每个元素执行有理数加法操作 */
for (k = 0; k < n; k++) {
npy_int64 x = *(npy_int64*)i0;
npy_int64 y = *(npy_int64*)i1;
*(rational*)o = rational_add(make_rational_fast(x, 1),
make_rational_fast(y, 1));
i0 += is0; i1 += is1; o += os;
}
}
for (k = 0; k < n; k++) {
rational x = *(rational*)i0;
rational y = *(rational*)i1;
*(rational*)o = rational_add(x, y);
i0 += is0;
i1 += is1;
o += os;
}
}
PyMethodDef module_methods[] = {
{0} /* sentinel */
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT, // 定义Python模块的头信息初始化
"_rational_tests", // 模块名为 "_rational_tests"
NULL, // 模块的文档字符串为空
-1, // 模块状态为 -1,表示使用子解释器支持
module_methods, // 模块方法定义,这里为空,用于定义模块中的函数
NULL, // 模块的全局状态,这里为空
NULL, // 模块的内存分配器,这里为空
NULL, // 模块的清理函数,这里为空
NULL // 模块的析构函数,这里为空
};
PyMODINIT_FUNC PyInit__rational_tests(void) {
PyObject *m = NULL; // Python对象指针m,初始化为空
PyObject* numpy_str; // Python对象指针numpy_str
PyObject* numpy; // Python对象指针numpy
int npy_rational; // 整型变量np_rational
import_array(); // 导入NumPy的数组接口
if (PyErr_Occurred()) { // 如果发生了Python错误
goto fail; // 跳转到失败标签处
}
import_umath(); // 导入NumPy的数学函数
if (PyErr_Occurred()) { // 如果发生了Python错误
goto fail; // 跳转到失败标签处
}
numpy_str = PyUnicode_FromString("numpy"); // 创建字符串对象"numpy"
if (!numpy_str) { // 如果字符串对象创建失败
goto fail; // 跳转到失败标签处
}
numpy = PyImport_Import(numpy_str); // 导入numpy模块
Py_DECREF(numpy_str); // 释放numpy_str的引用计数
if (!numpy) { // 如果numpy模块导入失败
goto fail; // 跳转到失败标签处
}
/* Can't set this until we import numpy */
PyRational_Type.tp_base = &PyGenericArrType_Type; // 设置PyRational_Type的基类为PyGenericArrType_Type
/* Initialize rational type object */
if (PyType_Ready(&PyRational_Type) < 0) { // 准备PyRational_Type类型对象
goto fail; // 跳转到失败标签处
}
/* Initialize rational descriptor */
PyArray_InitArrFuncs(&npyrational_arrfuncs); // 初始化npyrational_arrfuncs数组函数集
npyrational_arrfuncs.getitem = npyrational_getitem; // 设置npyrational_arrfuncs的getitem函数
npyrational_arrfuncs.setitem = npyrational_setitem; // 设置npyrational_arrfuncs的setitem函数
npyrational_arrfuncs.copyswapn = npyrational_copyswapn; // 设置npyrational_arrfuncs的copyswapn函数
npyrational_arrfuncs.copyswap = npyrational_copyswap; // 设置npyrational_arrfuncs的copyswap函数
npyrational_arrfuncs.compare = npyrational_compare; // 设置npyrational_arrfuncs的compare函数
npyrational_arrfuncs.argmin = npyrational_argmin; // 设置npyrational_arrfuncs的argmin函数
npyrational_arrfuncs.argmax = npyrational_argmax; // 设置npyrational_arrfuncs的argmax函数
npyrational_arrfuncs.dotfunc = npyrational_dot; // 设置npyrational_arrfuncs的dotfunc函数
npyrational_arrfuncs.nonzero = npyrational_nonzero; // 设置npyrational_arrfuncs的nonzero函数
npyrational_arrfuncs.fill = npyrational_fill; // 设置npyrational_arrfuncs的fill函数
npyrational_arrfuncs.fillwithscalar = npyrational_fillwithscalar; // 设置npyrational_arrfuncs的fillwithscalar函数
/* Left undefined: scanfunc, fromstr, sort, argsort */
Py_SET_TYPE(&npyrational_descr_proto, &PyArrayDescr_Type); // 设置npyrational_descr_proto的类型为PyArrayDescr_Type
npy_rational = PyArray_RegisterDataType(&npyrational_descr_proto); // 注册npyrational_descr_proto作为一种数据类型
if (npy_rational < 0) { // 如果注册失败
goto fail; // 跳转到失败标签处
}
PyArray_Descr *npyrational_descr = PyArray_DescrFromType(npy_rational); // 根据数据类型创建描述符对象
/* Support dtype(rational) syntax */
if (PyDict_SetItemString(PyRational_Type.tp_dict, "dtype",
(PyObject*)npyrational_descr) < 0) { // 将dtype(rational)语法支持添加到PyRational_Type的字典中
goto fail; // 跳转到失败标签处
}
/* Register casts to and from rational */
#define REGISTER_CAST(From,To,from_descr,to_typenum,safe) { \
PyArray_Descr* from_descr_##From##_##To = (from_descr); \
if (PyArray_RegisterCastFunc(from_descr_##From##_##To, \
(to_typenum), \
npycast_##From##_##To) < 0) { \
goto fail; \
} \
if (safe && PyArray_RegisterCanCast(from_descr_##From##_##To, \
(to_typenum), \
NPY_NOSCALAR) < 0) { \
goto fail; \
} \
}
#define REGISTER_INT_CASTS(bits) \
REGISTER_CAST(npy_int##bits, rational, \
PyArray_DescrFromType(NPY_INT##bits), npy_rational, 1) \
REGISTER_CAST(rational, npy_int##bits, npyrational_descr, \
NPY_INT##bits, 0)
注册整数类型的类型转换宏,包括从整数到有理数的转换和从有理数到整数的转换。
REGISTER_INT_CASTS(8)
注册8位整数类型的类型转换。
REGISTER_INT_CASTS(16)
注册16位整数类型的类型转换。
REGISTER_INT_CASTS(32)
注册32位整数类型的类型转换。
REGISTER_INT_CASTS(64)
注册64位整数类型的类型转换。
REGISTER_CAST(rational,float,npyrational_descr,NPY_FLOAT,0)
注册从有理数到单精度浮点数的类型转换。
REGISTER_CAST(rational,double,npyrational_descr,NPY_DOUBLE,1)
注册从有理数到双精度浮点数的类型转换。
REGISTER_CAST(npy_bool,rational, PyArray_DescrFromType(NPY_BOOL),
npy_rational,1)
注册从布尔类型到有理数的类型转换。
REGISTER_CAST(rational,npy_bool,npyrational_descr,NPY_BOOL,0)
注册从有理数到布尔类型的类型转换。
/* Register ufuncs */
#define REGISTER_UFUNC(name,...) { \
PyUFuncObject* ufunc = \
(PyUFuncObject*)PyObject_GetAttrString(numpy, #name); \
int _types[] = __VA_ARGS__; \
if (!ufunc) { \
goto fail; \
} \
if (sizeof(_types)/sizeof(int)!=ufunc->nargs) { \
PyErr_Format(PyExc_AssertionError, \
"ufunc %s takes %d arguments, our loop takes %lu", \
#name, ufunc->nargs, (unsigned long) \
(sizeof(_types)/sizeof(int))); \
Py_DECREF(ufunc); \
goto fail; \
} \
if (PyUFunc_RegisterLoopForType((PyUFuncObject*)ufunc, npy_rational, \
rational_ufunc_##name, _types, 0) < 0) { \
Py_DECREF(ufunc); \
goto fail; \
} \
Py_DECREF(ufunc); \
}
注册通用函数(ufunc),并为其注册有理数类型的循环执行函数。
#define REGISTER_UFUNC_BINARY_RATIONAL(name) \
REGISTER_UFUNC(name, {npy_rational, npy_rational, npy_rational})
注册二元有理数通用函数。
#define REGISTER_UFUNC_BINARY_COMPARE(name) \
REGISTER_UFUNC(name, {npy_rational, npy_rational, NPY_BOOL})
注册二元比较有理数通用函数。
#define REGISTER_UFUNC_UNARY(name) \
REGISTER_UFUNC(name, {npy_rational, npy_rational})
注册一元有理数通用函数。
/* Binary */
REGISTER_UFUNC_BINARY_RATIONAL(add)
注册二元有理数加法通用函数。
REGISTER_UFUNC_BINARY_RATIONAL(subtract)
注册二元有理数减法通用函数。
REGISTER_UFUNC_BINARY_RATIONAL(multiply)
注册二元有理数乘法通用函数。
REGISTER_UFUNC_BINARY_RATIONAL(divide)
注册二元有理数除法通用函数。
REGISTER_UFUNC_BINARY_RATIONAL(remainder)
注册二元有理数取余通用函数。
REGISTER_UFUNC_BINARY_RATIONAL(true_divide)
注册二元有理数真除法通用函数。
REGISTER_UFUNC_BINARY_RATIONAL(floor_divide)
注册二元有理数向下取整除法通用函数。
REGISTER_UFUNC_BINARY_RATIONAL(minimum)
注册二元有理数最小值通用函数。
REGISTER_UFUNC_BINARY_RATIONAL(maximum)
注册二元有理数最大值通用函数。
/* Comparisons */
REGISTER_UFUNC_BINARY_COMPARE(equal)
注册二元有理数等于比较通用函数。
REGISTER_UFUNC_BINARY_COMPARE(not_equal)
注册二元有理数不等于比较通用函数。
REGISTER_UFUNC_BINARY_COMPARE(less)
注册二元有理数小于比较通用函数。
REGISTER_UFUNC_BINARY_COMPARE(greater)
注册二元有理数大于比较通用函数。
REGISTER_UFUNC_BINARY_COMPARE(less_equal)
注册二元有理数小于等于比较通用函数。
REGISTER_UFUNC_BINARY_COMPARE(greater_equal)
注册二元有理数大于等于比较通用函数。
/* Unary */
REGISTER_UFUNC_UNARY(negative)
注册一元有理数负数通用函数。
REGISTER_UFUNC_UNARY(absolute)
注册一元有理数绝对值通用函数。
REGISTER_UFUNC_UNARY(floor)
注册一元有理数向下取整通用函数。
REGISTER_UFUNC_UNARY(ceil)
注册一元有理数向上取整通用函数。
REGISTER_UFUNC_UNARY(trunc)
注册一元有理数截断通用函数。
REGISTER_UFUNC_UNARY(rint)
注册一元有理数四舍五入通用函数。
REGISTER_UFUNC_UNARY(square)
注册一元有理数平方通用函数。
REGISTER_UFUNC_UNARY(reciprocal)
注册一元有理数倒数通用函数。
REGISTER_UFUNC_UNARY(sign)
注册一元有理数符号通用函数。
/* Create module */
m = PyModule_Create(&moduledef);
创建 Python 模块对象并使用指定的模块定义初始化它。
if (!m) {
// 如果模块指针 m 为 NULL,则跳转到失败标签
goto fail;
}
/* Add rational type */
// 增加有理数类型到模块 m 中
Py_INCREF(&PyRational_Type);
PyModule_AddObject(m,"rational",(PyObject*)&PyRational_Type);
/* Create matrix multiply generalized ufunc */
// 创建矩阵乘法通用函数
{
// 定义通用函数支持的数据类型数组
int types2[3] = {npy_rational,npy_rational,npy_rational};
// 创建矩阵乘法通用函数对象 gufunc
PyObject* gufunc = PyUFunc_FromFuncAndDataAndSignature(0,0,0,0,2,1,
PyUFunc_None,"matrix_multiply",
"return result of multiplying two matrices of rationals",
0,"(m,n),(n,p)->(m,p)");
// 如果创建失败,则跳转到失败标签
if (!gufunc) {
goto fail;
}
// 将有理数类型的循环注册到 gufunc 中
if (PyUFunc_RegisterLoopForType((PyUFuncObject*)gufunc, npy_rational,
rational_gufunc_matrix_multiply, types2, 0) < 0) {
goto fail;
}
// 将 gufunc 对象添加到模块 m 中
PyModule_AddObject(m,"matrix_multiply",(PyObject*)gufunc);
}
/* Create test ufunc with built in input types and rational output type */
// 创建具有内置输入类型和有理数输出类型的测试通用函数
{
// 定义测试通用函数支持的数据类型数组
int types3[3] = {NPY_INT64,NPY_INT64,npy_rational};
// 创建测试通用函数对象 ufunc
PyObject* ufunc = PyUFunc_FromFuncAndData(0,0,0,0,2,1,
PyUFunc_None,"test_add",
"add two matrices of int64 and return rational matrix",0);
// 如果创建失败,则跳转到失败标签
if (!ufunc) {
goto fail;
}
// 将有理数类型的循环注册到 ufunc 中
if (PyUFunc_RegisterLoopForType((PyUFuncObject*)ufunc, npy_rational,
rational_ufunc_test_add, types3, 0) < 0) {
goto fail;
}
// 将 ufunc 对象添加到模块 m 中
PyModule_AddObject(m,"test_add",(PyObject*)ufunc);
}
/* Create test ufunc with rational types using RegisterLoopForDescr */
// 使用 RegisterLoopForDescr 创建具有有理数类型的测试通用函数
{
// 创建测试通用函数对象 ufunc
PyObject* ufunc = PyUFunc_FromFuncAndData(0,0,0,0,2,1,
PyUFunc_None,"test_add_rationals",
"add two matrices of rationals and return rational matrix",0);
// 定义用于描述器的数据类型数组
PyArray_Descr* types[3] = {npyrational_descr,
npyrational_descr,
npyrational_descr};
// 如果创建失败,则跳转到失败标签
if (!ufunc) {
goto fail;
}
// 将有理数类型的循环注册到 ufunc 中
if (PyUFunc_RegisterLoopForDescr((PyUFuncObject*)ufunc, npyrational_descr,
rational_ufunc_test_add_rationals, types, 0) < 0) {
goto fail;
}
// 将 ufunc 对象添加到模块 m 中
PyModule_AddObject(m,"test_add_rationals",(PyObject*)ufunc);
}
/* Create numerator and denominator ufuncs */
// 创建分子和分母的通用函数
#define NEW_UNARY_UFUNC(name,type,doc) { \
// 定义通用函数支持的数据类型数组
int types[2] = {npy_rational,type}; \
// 创建通用函数对象 ufunc
PyObject* ufunc = PyUFunc_FromFuncAndData(0,0,0,0,1,1, \
PyUFunc_None,#name,doc,0); \
// 如果创建失败,则跳转到失败标签
if (!ufunc) { \
goto fail; \
} \
// 将有理数类型的循环注册到 ufunc 中
if (PyUFunc_RegisterLoopForType((PyUFuncObject*)ufunc, \
npy_rational,rational_ufunc_##name,types,0)<0) { \
goto fail; \
} \
// 将 ufunc 对象添加到模块 m 中
PyModule_AddObject(m,#name,(PyObject*)ufunc); \
}
// 创建分子通用函数对象并添加到模块 m 中
NEW_UNARY_UFUNC(numerator,NPY_INT64,"rational number numerator");
// 创建分母通用函数对象并添加到模块 m 中
NEW_UNARY_UFUNC(denominator,NPY_INT64,"rational number denominator");
/* Create gcd and lcm ufuncs */
# 定义宏 GCD_LCM_UFUNC,用于创建并注册名为 gcd 和 lcm 的通用函数对象到模块 m 中
# 这些函数处理整数类型 NPY_INT64,执行特定功能并提供相关文档说明
# 宏的展开部分,创建函数指针数组 func 和类型数组 types
# func 包含名为 gcd_ufunc 或 lcm_ufunc 的函数指针
# types 指定了函数接受和返回的参数类型都是 NPY_INT64
# 初始化数据指针数组 data,这里设为 NULL
# 使用 PyUFunc_FromFuncAndData 创建通用函数对象 ufunc
# 参数依次是 func、data、types、1、2、1、PyUFunc_One、#name、doc、0
# 如果创建失败,则跳转到标签 fail 处理错误
# 将创建的 ufunc 对象添加到模块 m 中,名称分别为 "gcd" 和 "lcm"
GCD_LCM_UFUNC(gcd,NPY_INT64,"greatest common denominator of two integers");
GCD_LCM_UFUNC(lcm,NPY_INT64,"least common multiple of two integers");
# 返回创建好的模块对象 m
return m;
fail:
# 如果当前没有 Python 异常发生
if (!PyErr_Occurred()) {
# 设置一个运行时错误的异常字符串,说明无法加载 _rational_tests 模块
PyErr_SetString(PyExc_RuntimeError,
"cannot load _rational_tests module.");
}
# 如果 m 非空(即 m 模块对象存在)
if (m) {
# 释放 m 模块对象的引用计数,并将 m 设置为 NULL
Py_DECREF(m);
m = NULL;
}
# 返回 m 模块对象(可能为 NULL)
return m;
}