NumPy 源码解析(六十)
.\numpy\numpy\_core\src\multiarray\common_dtype.c
/*
* 定义以下宏,以确保使用 NumPy 的最新 API 版本并禁用过时的 API。
* _MULTIARRAYMODULE 是为了在编译期间定义多维数组模块。
*/
/*
* 引入必要的头文件和库文件。
*/
/*
* 该文件定义了通用的“common dtype”操作逻辑。
* 这是复杂的,因为 NumPy 使用基于值的逻辑,并且没有明确的类型提升层次结构。
* 不像大多数语言中的 `int32 + float32 -> float64`,而是变为 `float32`。
* 另一个复杂的地方是基于值的提升,这意味着在许多情况下,Python 1 可能会成为 `int8` 或 `uint8`。
*
* 该文件实现了必要的逻辑,以便 `np.result_type(...)` 可以针对任何输入顺序给出正确的结果,并可以进一步推广到用户 DTypes。
*/
/*NUMPY_API
* 该函数定义了通用的 DType 运算符。
*
* 注意,通用的 DType 不会是 "object"(除非其中一个 DType 是 object),即使 object 可以在技术上正确表示所有值。
* 类似于 `np.result_type`,但适用于类而不是实例。
*
* TODO: 在暴露之前,我们应该审查返回值(例如,当没有找到通用的 DType 时不应报错)。
*
* @param dtype1 要找到通用类型的第一个 DType 类。
* @param dtype2 第二个 DType 类。
* @return 通用的 DType 或者 NULL 并设置错误。
*/
NPY_NO_EXPORT PyArray_DTypeMeta *
PyArray_CommonDType(PyArray_DTypeMeta *dtype1, PyArray_DTypeMeta *dtype2)
{
// 如果 dtype1 等于 dtype2,则增加引用计数并返回 dtype1
if (dtype1 == dtype2) {
Py_INCREF(dtype1);
return dtype1;
}
PyArray_DTypeMeta *common_dtype;
// 调用 NPY_DT_CALL_common_dtype 函数尝试找到 dtype1 和 dtype2 的通用 DType
common_dtype = NPY_DT_CALL_common_dtype(dtype1, dtype2);
// 如果未找到通用 DType,则尝试反转参数顺序再次查找
if (common_dtype == (PyArray_DTypeMeta *)Py_NotImplemented) {
Py_DECREF(common_dtype);
common_dtype = NPY_DT_CALL_common_dtype(dtype2, dtype1);
}
// 如果仍然未找到通用 DType,则返回 NULL
if (common_dtype == NULL) {
return NULL;
}
// 如果找到了 Py_NotImplemented,表示 dtype1 和 dtype2 没有通用 DType
if (common_dtype == (PyArray_DTypeMeta *)Py_NotImplemented) {
Py_DECREF(Py_NotImplemented);
// 报错并返回 NULL,指示没有找到通用 DType
PyErr_Format(npy_static_pydata.DTypePromotionError,
"The DTypes %S and %S do not have a common DType. "
"For example they cannot be stored in a single array unless "
"the dtype is `object`.", dtype1, dtype2);
return NULL;
}
// 返回找到的通用 DType
return common_dtype;
}
/**
* This function takes a list of dtypes and "reduces" them (in a sense,
* it finds the maximal dtype). Note that "maximum" here is defined by
* knowledge (or category or domain). A user DType must always "know"
* about all NumPy dtypes, floats "know" about integers, integers "know"
* about unsigned integers.
*
* c
* / \
* a \ <-- The actual promote(a, b) may be c or unknown.
* / \ \
* a b c
*
* The reduction is done "pairwise". In the above `a.__common_dtype__(b)`
* has a result (so `a` knows more) and `a.__common_dtype__(c)` returns
* NotImplemented (so `c` knows more). You may notice that the result
* `res = a.__common_dtype__(b)` is not important. We could try to use it
* to remove the whole branch if `res is c` or by checking if
* `c.__common_dtype__(res) is c`.
* Right now, we only clear initial elements in the most simple case where
* `a.__common_dtype__(b) is a` (and thus `b` cannot alter the end-result).
* Clearing means, we do not have to worry about them later.
*
* Abstract dtypes are not handled specially here. In a first
* version they were but this version also tried to be able to do value-based
* behavior.
* There may be some advantage to special casing the abstract ones (e.g.
* so that the concrete ones do not have to deal with it), but this would
* require more complex handling later on. See the logic in
* default_builtin_common_dtype
*
* @param length Number of DTypes
* @param dtypes List of DTypes
*/
static PyArray_DTypeMeta *
reduce_dtypes_to_most_knowledgeable(
npy_intp length, PyArray_DTypeMeta **dtypes)
{
assert(length >= 2); // Ensure the length is at least 2
npy_intp half = length / 2; // Calculate the half of the length
PyArray_DTypeMeta *res = NULL; // Initialize the result as NULL
for (npy_intp low = 0; low < half; low++) { // Loop through the first half of the length
npy_intp high = length - 1 - low; // Calculate the index of the opposite element
if (dtypes[high] == dtypes[low]) { // Check if the dtypes at the high and low indices are the same
/* Fast path for identical dtypes: do not call common_dtype */
Py_INCREF(dtypes[low]); // Increment the reference count of dtypes[low]
Py_XSETREF(res, dtypes[low]); // Set res to dtypes[low]
}
else {
Py_XSETREF(res, NPY_DT_CALL_common_dtype(dtypes[low], dtypes[high])); // Set res to the common dtype of dtypes[low] and dtypes[high]
if (res == NULL) { // If res is NULL
return NULL; // Return NULL
}
}
if (res == (PyArray_DTypeMeta *)Py_NotImplemented) { // Check if res is Py_NotImplemented
/* guess at other being more "knowledgable" */
PyArray_DTypeMeta *tmp = dtypes[low]; // Temporarily store dtypes[low]
dtypes[low] = dtypes[high]; // Set dtypes[low] to dtypes[high]
dtypes[high] = tmp; // Set dtypes[high] to the temporary value
}
else if (res == dtypes[low]) { // Check if res is equal to dtypes[low]
/* `dtypes[high]` cannot influence result: clear */
dtypes[high] = NULL; // Set dtypes[high] to NULL
}
}
if (length == 2) { // If the length is 2
return res; // Return res
}
Py_DECREF(res); // Decrease the reference count of res
return reduce_dtypes_to_most_knowledgeable(length - half, dtypes); // Recursively call the function with the updated length and dtypes
}
/*NUMPY_API
* Promotes a list of DTypes with each other in a way that should guarantee
* stable results even when changing the order. This function is smarter and
* can often return successful and unambiguous results when
* `common_dtype(common_dtype(dt1, dt2), dt3)` would depend on the operation
* order or fail. Nevertheless, DTypes should aim to ensure that their
* common-dtype implementation is associative and commutative! (Mainly,
* unsigned and signed integers are not.)
*
* For guaranteed consistent results DTypes must implement common-Dtype
* "transitively". If A promotes B and B promotes C, than A must generally
* also promote C; where "promotes" means implements the promotion. (There
* are some exceptions for abstract DTypes)
*
* In general this approach always works as long as the most generic dtype
* is either strictly larger, or compatible with all other dtypes.
* For example promoting float16 with any other float, integer, or unsigned
* integer again gives a floating point number. And any floating point number
* promotes in the "same way" as `float16`.
* If a user inserts more than one type into the NumPy type hierarchy, this
* can break. Given:
* uint24 + int32 -> int48
*
* The following becomes problematic (order does not matter):
* uint24 + int16 + uint32 -> int64
* <== (uint24 + int16) + (uint24 + uint32) -> int64
* <== int32 + uint32 -> int64
*
* It is impossible to achieve an `int48` result in the above.
*
* This is probably only resolvable by asking `uint24` to take over the
* whole reduction step; which we currently do not do.
* (It may be possible to notice the last up-cast and implement use something
* like: `uint24.nextafter(int32).__common_dtype__(uint32)`, but that seems
* even harder to grasp.)
*
* Note that a case where two dtypes are mixed (and know nothing about each
* other) will always generate an error:
* uint24 + int48 + int64 -> Error
*
* Even though `int64` is a safe solution, since `uint24 + int64 -> int64` and
* `int48 + int64 -> int64` and `int64` and there cannot be a smaller solution.
*
* //TODO: Maybe this function should allow not setting an error?
*
* @param length Number of dtypes (and values) must be at least 1
* @param dtypes The concrete or abstract DTypes to promote
* @return NULL or the promoted DType.
*/
NPY_NO_EXPORT PyArray_DTypeMeta *
PyArray_PromoteDTypeSequence(
npy_intp length, PyArray_DTypeMeta **dtypes_in)
{
// 如果只有一个dtype,直接返回该dtype,增加其引用计数
if (length == 1) {
Py_INCREF(dtypes_in[0]);
return dtypes_in[0];
}
// 否则初始化结果为NULL
PyArray_DTypeMeta *result = NULL;
/* Copy dtypes so that we can reorder them (only allocate when many) */
// 使用栈上的数组或堆上的数组来复制dtypes,以便重新排序(只有在长度大时才分配堆上内存)
PyObject *_scratch_stack[NPY_MAXARGS];
PyObject **_scratch_heap = NULL;
PyArray_DTypeMeta **dtypes = (PyArray_DTypeMeta **)_scratch_stack;
if (length > NPY_MAXARGS) {
_scratch_heap = PyMem_Malloc(length * sizeof(PyObject *));
// 如果分配内存失败,则设置内存错误并返回 NULL
if (_scratch_heap == NULL) {
PyErr_NoMemory();
return NULL;
}
// 将 _scratch_heap 强制转换为 PyArray_DTypeMeta 指针数组,用于存储传入的 dtypes
dtypes = (PyArray_DTypeMeta **)_scratch_heap;
}
// 将 dtypes_in 数组中的数据拷贝到 dtypes 数组中,拷贝的字节数为 length * sizeof(PyObject *)
memcpy(dtypes, dtypes_in, length * sizeof(PyObject *));
/*
* `result` 是最后的推广结果,通常情况下可以重复使用,除非它是 NotImplemneted。
* 传入的 dtypes 已部分排序,并在不再相关时已清除。
* `dtypes[0]` 将是最有知识(最高类别)的 dtype,这里我们称之为 "main_dtype"。
*/
// 调用 reduce_dtypes_to_most_knowledgeable 函数处理 dtypes,返回推广后的结果给 result
result = reduce_dtypes_to_most_knowledgeable(length, dtypes);
// 如果 result 为 NULL,则跳转至 finish 标签处
if (result == NULL) {
goto finish;
}
// 将 dtypes[0] 赋值给 main_dtype,表示最有知识的 dtype
PyArray_DTypeMeta *main_dtype = dtypes[0];
// reduce_start 初始化为 1
npy_intp reduce_start = 1;
// 如果 result 是 Py_NotImplemented,则将 result 设置为 NULL
if (result == (PyArray_DTypeMeta *)Py_NotImplemented) {
Py_SETREF(result, NULL);
}
else {
/* (new) first value is already taken care of in `result` */
// 否则,reduce_start 设置为 2,表示处理的起始位置
reduce_start = 2;
}
/*
* 到此为止,我们最多只查看了每个 DType 一次。
* `main_dtype` 必须了解所有其他 dtype(否则将会失败),
* 并且其 `common_dtype` 返回的所有 dtype 必须保证能够互相推广成功。
* 在这一点上,"main DType" 的任务是确保顺序无关紧要。
* 如果这证明是一个限制,这种 "reduction" 将必须变成一个默认版本,并允许 DType 来覆盖它。
*/
// prev 初始化为 NULL
PyArray_DTypeMeta *prev = NULL;
`
for (npy_intp i = reduce_start; i < length; i++) {
// 循环遍历从 reduce_start 开始的 dtypes 数组
if (dtypes[i] == NULL || dtypes[i] == prev) {
// 如果当前 dtypes[i] 是 NULL 或者与前一个相同,则跳过当前循环
continue;
}
/*
* 将当前 dtype 与主 dtype 进行"提升"(promotion),假设结果不会低于主 dtype 的类别。
*/
PyArray_DTypeMeta *promotion = NPY_DT_CALL_common_dtype(
main_dtype, dtypes[i]);
if (promotion == NULL) {
// 如果提升失败,则设置 result 为 NULL,并跳转至 finish 标签处
Py_XSETREF(result, NULL);
goto finish;
}
else if ((PyObject *)promotion == Py_NotImplemented) {
// 如果提升操作返回 Py_NotImplemented,则处理错误情况
Py_DECREF(Py_NotImplemented);
Py_XSETREF(result, NULL);
PyObject *dtypes_in_tuple = PyTuple_New(length);
if (dtypes_in_tuple == NULL) {
goto finish;
}
// 构建一个包含所有 dtypes 的元组
for (npy_intp l=0; l < length; l++) {
Py_INCREF(dtypes_in[l]);
PyTuple_SET_ITEM(dtypes_in_tuple, l, (PyObject *)dtypes_in[l]);
}
// 设置错误信息并跳转至 finish 标签处
PyErr_Format(npy_static_pydata.DTypePromotionError,
"The DType %S could not be promoted by %S. This means that "
"no common DType exists for the given inputs. "
"For example they cannot be stored in a single array unless "
"the dtype is `object`. The full list of DTypes is: %S",
dtypes[i], main_dtype, dtypes_in_tuple);
Py_DECREF(dtypes_in_tuple);
goto finish;
}
if (result == NULL) {
// 如果 result 为 NULL,则将其设置为当前的 promotion
result = promotion;
continue;
}
/*
* 以上步骤完成提升后,现在与当前的 result 进行"减少"(reduce)操作;
* 注意在典型情况下,我们预期这一步骤不会产生实际操作。
*/
Py_SETREF(result, PyArray_CommonDType(result, promotion));
Py_DECREF(promotion);
if (result == NULL) {
// 如果操作后 result 为 NULL,则跳转至 finish 标签处
goto finish;
}
}
finish:
// 释放临时内存
PyMem_Free(_scratch_heap);
// 返回最终的 result
return result;
}
.\numpy\numpy\_core\src\multiarray\compiled_base.c
/*
* 定义 NPY_NO_DEPRECATED_API 为 NPY_API_VERSION,避免使用已弃用的 API
* 定义 _MULTIARRAYMODULE
*/
/*
* 引入必要的头文件:
* - Python.h:Python C API 的核心头文件
* - structmember.h:定义结构体成员的相关宏和函数
* - arrayobject.h:NumPy 多维数组对象的头文件
* - npy_3kcompat.h:NumPy 兼容 Python 3 的头文件
* - npy_math.h:NumPy 数学运算的头文件
* - npy_argparse.h:NumPy 参数解析相关的头文件
* - npy_config.h:NumPy 的配置文件
* - templ_common.h:用于模板通用操作的头文件,如 npy_mul_sizes_with_overflow
* - lowlevel_strided_loops.h:低级别分块循环的头文件,如 npy_bswap8
* - alloc.h:内存分配相关的头文件
* - ctors.h:构造函数相关的头文件
* - common.h:通用工具函数的头文件
* - dtypemeta.h:数据类型元信息的头文件
* - simd/simd.h:SIMD(单指令多数据)操作的头文件
* - string.h:C 标准字符串操作的头文件
*/
/*
* 定义枚举 PACK_ORDER,用于表示打包顺序:
* - PACK_ORDER_LITTLE:小端顺序
* - PACK_ORDER_BIG:大端顺序
*/
typedef enum {
PACK_ORDER_LITTLE = 0,
PACK_ORDER_BIG
} PACK_ORDER;
/*
* 检查数组是否单调的函数
* 返回值:
* -1:数组单调递减
* +1:数组单调递增
* 0:数组不单调
*/
static int
check_array_monotonic(const double *a, npy_intp lena)
{
npy_intp i;
double next;
double last;
if (lena == 0) {
/* 如果数组长度为0,所有的元素都相同,认为是单调递增 */
return 1;
}
last = a[0];
/* 跳过数组开头的重复值 */
for (i = 1; (i < lena) && (a[i] == last); i++);
if (i == lena) {
/* 如果跳过重复值后数组长度为0,所有的元素都相同,认为是单调递增 */
return 1;
}
next = a[i];
if (last < next) {
/* 可能是单调递增 */
for (i += 1; i < lena; i++) {
last = next;
next = a[i];
if (last > next) {
return 0;
}
}
return 1;
}
else {
/* last > next,可能是单调递减 */
for (i += 1; i < lena; i++) {
last = next;
next = a[i];
if (last < next) {
return 0;
}
}
return -1;
}
}
/*
* 找到整数数组的最小值和最大值的函数
*/
static void
minmax(const npy_intp *data, npy_intp data_len, npy_intp *mn, npy_intp *mx)
{
npy_intp min = *data;
npy_intp max = *data;
while (--data_len) {
const npy_intp val = *(++data);
if (val < min) {
min = val;
}
else if (val > max) {
max = val;
}
}
*mn = min;
*mx = max;
}
/*
* 注册为 bincount 的 arr_bincount 函数
* bincount 接受一个、两个或三个参数:
* - 第一个参数是非负整数数组
* - 第二个参数是权重数组(如果有),必须能够提升为 double 类型
* - 第三个参数(如果有)是期望输出数组的最小长度
* 如果没有权重数组,bincount(list)[i] 表示在 list 中出现 i 的次数;
* 如果有权重数组,则 bincount(self, list, weight)[i] 表示所有 list[j] == i 的权重和。
* 不使用 Self 参数。
*/
NPY_NO_EXPORT PyObject *
arr_bincount(PyObject *NPY_UNUSED(self), PyObject *const *args,
Py_ssize_t len_args, PyObject *kwnames)
{
// 声明变量,用于存储函数参数和结果
PyObject *list = NULL, *weight = Py_None, *mlength = NULL;
PyArrayObject *lst = NULL, *ans = NULL, *wts = NULL;
npy_intp *numbers, *ians, len, mx, mn, ans_size;
npy_intp minlength = 0;
npy_intp i;
double *weights , *dans;
// 解析函数参数
NPY_PREPARE_ARGPARSER;
if (npy_parse_arguments("bincount", args, len_args, kwnames,
"list", NULL, &list,
"|weights", NULL, &weight,
"|minlength", NULL, &mlength,
NULL, NULL, NULL) < 0) {
return NULL;
}
// 将传入的列表参数转换为一维整型数组对象
lst = (PyArrayObject *)PyArray_ContiguousFromAny(list, NPY_INTP, 1, 1);
if (lst == NULL) {
goto fail;
}
len = PyArray_SIZE(lst);
/*
* This if/else if can be removed by changing the argspec to O|On above,
* once we retire the deprecation
*/
// 处理 minlength 参数的特殊情况
if (mlength == Py_None) {
/* NumPy 1.14, 2017-06-01 */
// 发出弃用警告,建议传入 0 作为 minlength 而不是 None
if (DEPRECATE("0 should be passed as minlength instead of None; "
"this will error in future.") < 0) {
goto fail;
}
}
else if (mlength != NULL) {
// 将传入的 minlength 参数转换为 npy_intp 类型
minlength = PyArray_PyIntAsIntp(mlength);
if (error_converting(minlength)) {
goto fail;
}
}
// 检查 minlength 是否为负数,若是则报错并跳转到错误处理部分
if (minlength < 0) {
PyErr_SetString(PyExc_ValueError,
"'minlength' must not be negative");
goto fail;
}
// 处理空列表的情况
if (len == 0) {
// 创建一个长度为 minlength 的全零数组对象
ans = (PyArrayObject *)PyArray_ZEROS(1, &minlength, NPY_INTP, 0);
if (ans == NULL){
goto fail;
}
// 释放 lst 对象,并返回结果数组对象
Py_DECREF(lst);
return (PyObject *)ans;
}
// 获取列表数据的指针,并计算列表中的最小值和最大值
numbers = (npy_intp *)PyArray_DATA(lst);
minmax(numbers, len, &mn, &mx);
// 检查列表中是否有负数,若有则报错并跳转到错误处理部分
if (mn < 0) {
PyErr_SetString(PyExc_ValueError,
"'list' argument must have no negative elements");
goto fail;
}
// 计算结果数组的大小
ans_size = mx + 1;
// 如果传入了 minlength 参数且 ans_size 小于 minlength,则以 minlength 为准
if (mlength != Py_None) {
if (ans_size < minlength) {
ans_size = minlength;
}
}
// 处理未传入权重参数的情况
if (weight == Py_None) {
// 创建一个长度为 ans_size 的全零数组对象
ans = (PyArrayObject *)PyArray_ZEROS(1, &ans_size, NPY_INTP, 0);
if (ans == NULL) {
goto fail;
}
// 获取结果数组的数据指针,开启线程,并根据列表中的元素值进行计数
ians = (npy_intp *)PyArray_DATA(ans);
NPY_BEGIN_ALLOW_THREADS;
for (i = 0; i < len; i++)
ians[numbers[i]] += 1;
NPY_END_ALLOW_THREADS;
// 释放 lst 对象
Py_DECREF(lst);
}
// 如果不是首次调用该函数,则执行以下操作
else {
// 从给定对象 `weight` 创建一个连续的 `PyArrayObject` 对象,数据类型为双精度浮点数
wts = (PyArrayObject *)PyArray_ContiguousFromAny(
weight, NPY_DOUBLE, 1, 1);
// 如果 `wts` 为 NULL,则跳转到失败处理标签
if (wts == NULL) {
goto fail;
}
// 获取 `wts` 对象的数据指针,并赋给 `weights`
weights = (double *)PyArray_DATA(wts);
// 如果 `wts` 对象的大小与 `len` 不相等,则设置错误信息并跳转到失败处理标签
if (PyArray_SIZE(wts) != len) {
PyErr_SetString(PyExc_ValueError,
"The weights and list don't have the same length.");
goto fail;
}
// 创建一个元素个数为 `ans_size` 的双精度浮点数类型的零数组 `ans`
ans = (PyArrayObject *)PyArray_ZEROS(1, &ans_size, NPY_DOUBLE, 0);
// 如果 `ans` 为 NULL,则跳转到失败处理标签
if (ans == NULL) {
goto fail;
}
// 获取 `ans` 对象的数据指针,并赋给 `dans`
dans = (double *)PyArray_DATA(ans);
// 开始线程允许,用于多线程环境下的线程安全操作
NPY_BEGIN_ALLOW_THREADS;
// 遍历长度为 `len` 的循环
for (i = 0; i < len; i++) {
// 将 `weights[i]` 的值加到 `dans[numbers[i]]` 上
dans[numbers[i]] += weights[i];
}
// 结束线程允许,恢复线程锁状态
NPY_END_ALLOW_THREADS;
// 释放 `lst` 和 `wts` 对象的引用
Py_DECREF(lst);
Py_DECREF(wts);
}
// 返回 `ans` 对象的 PyObject 指针形式
return (PyObject *)ans;
fail:
// 释放 lst 对象的引用计数
Py_XDECREF(lst);
// 释放 wts 对象的引用计数
Py_XDECREF(wts);
// 释放 ans 对象的引用计数
Py_XDECREF(ans);
// 返回 NULL 表示函数执行失败
return NULL;
}
/* Internal function to expose check_array_monotonic to python */
// 定义一个不导出的函数,将 check_array_monotonic 暴露给 Python
NPY_NO_EXPORT PyObject *
arr__monotonicity(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"x", NULL};
PyObject *obj_x = NULL;
PyArrayObject *arr_x = NULL;
long monotonic;
npy_intp len_x;
NPY_BEGIN_THREADS_DEF;
// 解析参数,期望参数是一个对象和一个关键字参数列表
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:_monotonicity", kwlist,
&obj_x)) {
// 解析失败,返回 NULL
return NULL;
}
/*
* TODO:
* `x` could be strided, needs change to check_array_monotonic
* `x` is forced to double for this check
*/
// 将 obj_x 转换为一个一维的 NPY_DOUBLE 类型的 PyArrayObject,以供后续的单调性检查使用
arr_x = (PyArrayObject *)PyArray_FROMANY(
obj_x, NPY_DOUBLE, 1, 1, NPY_ARRAY_CARRAY_RO);
if (arr_x == NULL) {
// 转换失败,返回 NULL
return NULL;
}
len_x = PyArray_SIZE(arr_x); // 获取数组 arr_x 的大小
NPY_BEGIN_THREADS_THRESHOLDED(len_x)
monotonic = check_array_monotonic(
(const double *)PyArray_DATA(arr_x), len_x); // 调用 check_array_monotonic 函数进行单调性检查
NPY_END_THREADS
Py_DECREF(arr_x); // 释放 arr_x 对象的引用计数
// 将检查结果 monotonic 转换为 Python 的 long 类型,并返回
return PyLong_FromLong(monotonic);
}
/*
* Returns input array with values inserted sequentially into places
* indicated by the mask
*/
// 返回一个在 mask 指示的位置上顺序插入值的输入数组
NPY_NO_EXPORT PyObject *
arr_place(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict)
{
char *src, *dest;
npy_bool *mask_data;
PyArray_Descr *dtype;
PyArray_CopySwapFunc *copyswap;
PyObject *array0, *mask0, *values0;
PyArrayObject *array, *mask, *values;
npy_intp i, j, chunk, nm, ni, nv;
static char *kwlist[] = {"input", "mask", "vals", NULL};
NPY_BEGIN_THREADS_DEF;
values = mask = NULL;
// 解析参数,期望参数是一个 PyArray_Type 类型的对象,以及两个任意对象
if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O!OO:place", kwlist,
&PyArray_Type, &array0, &mask0, &values0)) {
// 解析失败,返回 NULL
return NULL;
}
// 将 array0 转换为一个 NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY 类型的 PyArrayObject 对象
array = (PyArrayObject *)PyArray_FromArray((PyArrayObject *)array0, NULL,
NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY);
if (array == NULL) {
goto fail; // 转换失败,跳转到 fail 标签处理异常
}
ni = PyArray_SIZE(array); // 获取数组 array 的大小
dest = PyArray_DATA(array); // 获取数组 array 的数据起始地址
chunk = PyArray_ITEMSIZE(array); // 获取数组 array 的每个元素大小
// 将 mask0 转换为一个 NPY_BOOL 类型的 PyArrayObject 对象
mask = (PyArrayObject *)PyArray_FROM_OTF(mask0, NPY_BOOL,
NPY_ARRAY_CARRAY | NPY_ARRAY_FORCECAST);
if (mask == NULL) {
goto fail; // 转换失败,跳转到 fail 标签处理异常
}
nm = PyArray_SIZE(mask); // 获取数组 mask 的大小
if (nm != ni) {
// mask 和 array 大小不匹配,抛出 ValueError 异常
PyErr_SetString(PyExc_ValueError,
"place: mask and data must be "
"the same size");
goto fail; // 跳转到 fail 标签处理异常
}
mask_data = PyArray_DATA(mask); // 获取数组 mask 的数据起始地址
dtype = PyArray_DESCR(array); // 获取数组 array 的数据描述符
Py_INCREF(dtype); // 增加数据描述符的引用计数
// 将 values0 转换为一个 NPY_ARRAY_CARRAY 类型的 PyArrayObject 对象
values = (PyArrayObject *)PyArray_FromAny(values0, dtype,
0, 0, NPY_ARRAY_CARRAY, NULL);
if (values == NULL) {
goto fail; // 转换失败,跳转到 fail 标签处理异常
}
nv = PyArray_SIZE(values); // 获取数组 values 的大小(如果是空数组,则为零)
if (nv <= 0):
npy_bool allFalse = 1;
i = 0;
while (allFalse && i < ni):
if (mask_data[i]):
allFalse = 0;
else:
i++;
if (!allFalse):
PyErr_SetString(PyExc_ValueError,
"Cannot insert from an empty array!");
goto fail;
else:
Py_XDECREF(values);
Py_XDECREF(mask);
PyArray_ResolveWritebackIfCopy(array);
Py_XDECREF(array);
Py_RETURN_NONE;
src = PyArray_DATA(values);
j = 0;
copyswap = PyDataType_GetArrFuncs(PyArray_DESCR(array))->copyswap;
NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(array));
for (i = 0; i < ni; i++) {
if (mask_data[i]):
if (j >= nv):
j = 0;
copyswap(dest + i*chunk, src + j*chunk, 0, array);
j++;
}
NPY_END_THREADS;
Py_XDECREF(values);
Py_XDECREF(mask);
PyArray_ResolveWritebackIfCopy(array);
Py_DECREF(array);
Py_RETURN_NONE;
fail:
Py_XDECREF(mask);
PyArray_ResolveWritebackIfCopy(array);
Py_XDECREF(array);
Py_XDECREF(values);
return NULL;
}
/**
* @brief Perform a linear search in a sorted array to find the largest index
* such that the array element is less than or equal to the given key.
*
* @param key Key value to search for.
* @param arr Sorted array to search within.
* @param len Length of the array.
* @param i0 Starting index for the search.
* @return Largest index i such that arr[i] <= key.
*/
static inline npy_intp
_linear_search(const npy_double key, const npy_double *arr, const npy_intp len, const npy_intp i0)
{
npy_intp i;
for (i = i0; i < len && key >= arr[i]; i++);
return i - 1;
}
/**
* @brief Perform a binary search in a sorted array to find the index such that
* arr[i] <= key < arr[i + 1].
*
* If a starting index guess is provided, it checks nearby values first.
* Otherwise, it defaults to bisection method for finding the index.
*
* @param key Key value to search for.
* @param arr Sorted array to search within.
* @param len Length of the array.
* @param guess Initial guess for the index.
* @return Index i such that arr[i] <= key < arr[i + 1].
*/
static npy_intp
binary_search_with_guess(const npy_double key, const npy_double *arr,
npy_intp len, npy_intp guess)
{
npy_intp imin = 0;
npy_intp imax = len;
/* Handle keys outside of the arr range first */
if (key > arr[len - 1]) {
return len;
}
else if (key < arr[0]) {
return -1;
}
/*
* If len <= 4 use linear search.
* From above we know key >= arr[0] when we start.
*/
if (len <= 4) {
return _linear_search(key, arr, len, 1);
}
if (guess > len - 3) {
guess = len - 3;
}
if (guess < 1) {
guess = 1;
}
/* check most likely values: guess - 1, guess, guess + 1 */
if (key < arr[guess]) {
if (key < arr[guess - 1]) {
imax = guess - 1;
/* last attempt to restrict search to items in cache */
if (guess > LIKELY_IN_CACHE_SIZE &&
key >= arr[guess - LIKELY_IN_CACHE_SIZE]) {
imin = guess - LIKELY_IN_CACHE_SIZE;
}
}
else {
/* key >= arr[guess - 1] */
return guess - 1;
}
}
else {
/* key >= arr[guess] */
if (key < arr[guess + 1]) {
return guess;
}
else {
/* key >= arr[guess + 1] */
if (key < arr[guess + 2]) {
return guess + 1;
}
else {
/* key >= arr[guess + 2] */
imin = guess + 2;
/* last attempt to restrict search to items in cache */
if (guess < len - LIKELY_IN_CACHE_SIZE - 1 &&
key < arr[guess + LIKELY_IN_CACHE_SIZE]) {
imax = guess + LIKELY_IN_CACHE_SIZE;
}
}
}
}
/* finally, find index by bisection */
while (imin < imax) {
const npy_intp imid = imin + ((imax - imin) >> 1);
if (key >= arr[imid]) {
imin = imid + 1;
}
else {
imax = imid;
}
}
return imin - 1;
}
NPY_NO_EXPORT PyObject *
arr_interp(PyObject *NPY_UNUSED(self), PyObject *const *args, Py_ssize_t len_args,
PyObject *kwnames)
{
// 指针声明
PyObject *fp, *xp, *x;
// 左右边界初始化为 NULL
PyObject *left = NULL, *right = NULL;
// 数组对象声明
PyArrayObject *afp = NULL, *axp = NULL, *ax = NULL, *af = NULL;
// 索引变量声明
npy_intp i, lenx, lenxp;
// 左右边界值声明
npy_double lval, rval;
// 指向数据的指针声明
const npy_double *dy, *dx, *dz;
npy_double *dres, *slopes = NULL;
// 线程操作宏定义
NPY_BEGIN_THREADS_DEF;
// 参数解析准备
NPY_PREPARE_ARGPARSER;
// 解析函数参数,如果失败返回 NULL
if (npy_parse_arguments("interp", args, len_args, kwnames,
"x", NULL, &x,
"xp", NULL, &xp,
"fp", NULL, &fp,
"|left", NULL, &left,
"|right", NULL, &right,
NULL, NULL, NULL) < 0) {
return NULL;
}
// 将 fp 转换为 NPY_DOUBLE 类型的连续数组对象
afp = (PyArrayObject *)PyArray_ContiguousFromAny(fp, NPY_DOUBLE, 1, 1);
if (afp == NULL) {
return NULL;
}
// 将 xp 转换为 NPY_DOUBLE 类型的连续数组对象
axp = (PyArrayObject *)PyArray_ContiguousFromAny(xp, NPY_DOUBLE, 1, 1);
if (axp == NULL) {
goto fail;
}
// 将 x 转换为 NPY_DOUBLE 类型的数组对象
ax = (PyArrayObject *)PyArray_ContiguousFromAny(x, NPY_DOUBLE, 0, 0);
if (ax == NULL) {
goto fail;
}
// 获取 axp 的长度
lenxp = PyArray_SIZE(axp);
// 如果长度为 0,抛出异常并跳转到失败处理标签
if (lenxp == 0) {
PyErr_SetString(PyExc_ValueError,
"array of sample points is empty");
goto fail;
}
// 检查 afp 和 axp 的长度是否相同,如果不同则抛出异常并跳转到失败处理标签
if (PyArray_SIZE(afp) != lenxp) {
PyErr_SetString(PyExc_ValueError,
"fp and xp are not of the same length.");
goto fail;
}
// 创建一个与 ax 具有相同维度和形状的 NPY_DOUBLE 类型的新数组对象 af
af = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(ax),
PyArray_DIMS(ax), NPY_DOUBLE);
if (af == NULL) {
goto fail;
}
// 获取 ax 的长度
lenx = PyArray_SIZE(ax);
// 获取 afp、axp、ax、af 数组对象的数据指针
dy = (const npy_double *)PyArray_DATA(afp);
dx = (const npy_double *)PyArray_DATA(axp);
dz = (const npy_double *)PyArray_DATA(ax);
dres = (npy_double *)PyArray_DATA(af);
/* 获取左右填充值。*/
if ((left == NULL) || (left == Py_None)) {
lval = dy[0];
}
else {
lval = PyFloat_AsDouble(left);
// 如果转换失败则跳转到失败处理标签
if (error_converting(lval)) {
goto fail;
}
}
if ((right == NULL) || (right == Py_None)) {
rval = dy[lenxp - 1];
}
else {
rval = PyFloat_AsDouble(right);
// 如果转换失败则跳转到失败处理标签
if (error_converting(rval)) {
goto fail;
}
}
/* binary_search_with_guess 至少需要一个长度为 3 的数组 */
if (lenxp == 1) {
const npy_double xp_val = dx[0];
const npy_double fp_val = dy[0];
// 多线程处理,根据 lenx 的大小决定是否启动多线程
NPY_BEGIN_THREADS_THRESHOLDED(lenx);
// 对于每个 x 中的元素进行插值计算
for (i = 0; i < lenx; ++i) {
const npy_double x_val = dz[i];
dres[i] = (x_val < xp_val) ? lval :
((x_val > xp_val) ? rval : fp_val);
}
// 结束多线程处理
NPY_END_THREADS;
}
else {
npy_intp j = 0;
/* only pre-calculate slopes if there are relatively few of them. */
如果斜率相对较少,则预先计算斜率
if (lenxp <= lenx) {
// 分配内存以存储斜率数组,长度为 (lenxp - 1)
slopes = PyArray_malloc((lenxp - 1) * sizeof(npy_double));
// 如果分配失败,报告内存错误并跳转到失败处理部分
if (slopes == NULL) {
PyErr_NoMemory();
goto fail;
}
}
NPY_BEGIN_THREADS; // 开始线程安全区域
// 如果斜率数组非空,计算每段斜率
if (slopes != NULL) {
for (i = 0; i < lenxp - 1; ++i) {
slopes[i] = (dy[i+1] - dy[i]) / (dx[i+1] - dx[i]);
}
}
// 对每个需要插值的点进行处理
for (i = 0; i < lenx; ++i) {
const npy_double x_val = dz[i];
// 如果 x_val 是 NaN,直接将结果设为 x_val 并继续下一个点
if (npy_isnan(x_val)) {
dres[i] = x_val;
continue;
}
// 使用二分查找找到 x_val 在 dx 中的位置,j 是初始猜测位置
j = binary_search_with_guess(x_val, dx, lenxp, j);
// 根据查找结果决定如何插值
if (j == -1) {
dres[i] = lval; // 如果找不到合适位置,结果设为左边界值 lval
}
else if (j == lenxp) {
dres[i] = rval; // 如果超出右边界,结果设为右边界值 rval
}
else if (j == lenxp - 1) {
dres[i] = dy[j]; // 如果在最后一个点上,结果直接设为对应的 dy 值
}
else if (dx[j] == x_val) {
/* Avoid potential non-finite interpolation */
// 如果精确找到了点,避免潜在的非有限插值问题,结果直接设为对应的 dy 值
dres[i] = dy[j];
}
else {
const npy_double slope =
(slopes != NULL) ? slopes[j] :
(dy[j+1] - dy[j]) / (dx[j+1] - dx[j]);
// 使用线性插值计算结果
dres[i] = slope*(x_val - dx[j]) + dy[j];
// 如果插值结果是 NaN,尝试使用相邻点进行插值
if (NPY_UNLIKELY(npy_isnan(dres[i]))) {
dres[i] = slope*(x_val - dx[j+1]) + dy[j+1];
// 如果再次插值结果仍然是 NaN,并且相邻点的 dy 值相同,则结果设为相邻点的 dy 值
if (NPY_UNLIKELY(npy_isnan(dres[i])) && dy[j] == dy[j+1]) {
dres[i] = dy[j];
}
}
}
}
NPY_END_THREADS; // 结束线程安全区域
}
// 释放斜率数组内存
PyArray_free(slopes);
// 释放引用的数组对象
Py_DECREF(afp);
Py_DECREF(axp);
Py_DECREF(ax);
// 返回插值结果数组对象
return PyArray_Return(af);
fail:
// 释放 afp 指针所指向的对象,并将其引用计数减少,避免内存泄漏
Py_XDECREF(afp);
// 释放 axp 指针所指向的对象,并将其引用计数减少,避免内存泄漏
Py_XDECREF(axp);
// 释放 ax 指针所指向的对象,并将其引用计数减少,避免内存泄漏
Py_XDECREF(ax);
// 释放 af 指针所指向的对象,并将其引用计数减少,避免内存泄漏
Py_XDECREF(af);
// 返回 NULL 表示函数执行失败
return NULL;
}
/* As for arr_interp but for complex fp values */
NPY_NO_EXPORT PyObject *
arr_interp_complex(PyObject *NPY_UNUSED(self), PyObject *const *args, Py_ssize_t len_args,
PyObject *kwnames)
{
// 声明需要使用的变量
PyObject *fp, *xp, *x;
PyObject *left = NULL, *right = NULL;
PyArrayObject *afp = NULL, *axp = NULL, *ax = NULL, *af = NULL;
npy_intp i, lenx, lenxp;
const npy_double *dx, *dz;
const npy_cdouble *dy;
npy_cdouble lval, rval;
npy_cdouble *dres, *slopes = NULL;
NPY_BEGIN_THREADS_DEF;
// 准备解析参数
NPY_PREPARE_ARGPARSER;
if (npy_parse_arguments("interp_complex", args, len_args, kwnames,
"x", NULL, &x,
"xp", NULL, &xp,
"fp", NULL, &fp,
"|left", NULL, &left,
"|right", NULL, &right,
NULL, NULL, NULL) < 0) {
// 解析参数失败,返回 NULL 表示函数执行失败
return NULL;
}
// 将 fp 转换为 NPY_CDOUBLE 类型的连续数组对象
afp = (PyArrayObject *)PyArray_ContiguousFromAny(fp, NPY_CDOUBLE, 1, 1);
if (afp == NULL) {
// 转换失败,返回 NULL 表示函数执行失败
return NULL;
}
// 将 xp 转换为 NPY_DOUBLE 类型的连续数组对象
axp = (PyArrayObject *)PyArray_ContiguousFromAny(xp, NPY_DOUBLE, 1, 1);
if (axp == NULL) {
// 转换失败,跳转到 fail 标签处处理错误
goto fail;
}
// 将 x 转换为 NPY_DOUBLE 类型的连续数组对象
ax = (PyArrayObject *)PyArray_ContiguousFromAny(x, NPY_DOUBLE, 0, 0);
if (ax == NULL) {
// 转换失败,跳转到 fail 标签处处理错误
goto fail;
}
lenxp = PyArray_SIZE(axp);
if (lenxp == 0) {
// xp 数组长度为 0,抛出 ValueError 异常并跳转到 fail 标签处处理错误
PyErr_SetString(PyExc_ValueError,
"array of sample points is empty");
goto fail;
}
if (PyArray_SIZE(afp) != lenxp) {
// fp 和 xp 数组长度不相等,抛出 ValueError 异常并跳转到 fail 标签处处理错误
PyErr_SetString(PyExc_ValueError,
"fp and xp are not of the same length.");
goto fail;
}
lenx = PyArray_SIZE(ax);
dx = (const npy_double *)PyArray_DATA(axp);
dz = (const npy_double *)PyArray_DATA(ax);
// 创建一个新的 NPY_CDOUBLE 类型的数组对象 af
af = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(ax),
PyArray_DIMS(ax), NPY_CDOUBLE);
if (af == NULL) {
// 创建失败,跳转到 fail 标签处处理错误
goto fail;
}
dy = (const npy_cdouble *)PyArray_DATA(afp);
dres = (npy_cdouble *)PyArray_DATA(af);
/* 获取 left 和 right 填充值 */
if ((left == NULL) || (left == Py_None)) {
// 如果 left 为 NULL 或者 Py_None,则将 lval 设置为 dy[0]
lval = dy[0];
}
else {
// 否则,从 left 中获取实部和虚部,并进行类型转换,如果转换失败则跳转到 fail 标签处理错误
npy_csetreal(&lval, PyComplex_RealAsDouble(left));
if (error_converting(npy_creal(lval))) {
goto fail;
}
npy_csetimag(&lval, PyComplex_ImagAsDouble(left));
if (error_converting(npy_cimag(lval))) {
goto fail;
}
}
if ((right == NULL) || (right == Py_None)) {
// 如果 right 为 NULL 或者 Py_None,则将 rval 设置为 dy[lenxp - 1]
rval = dy[lenxp - 1];
}
else {
// 否则,从 right 中获取实部和虚部,并进行类型转换,如果转换失败则跳转到 fail 标签处理错误
npy_csetreal(&rval, PyComplex_RealAsDouble(right));
if (error_converting(npy_creal(rval))) {
goto fail;
}
npy_csetimag(&rval, PyComplex_ImagAsDouble(right));
if (error_converting(npy_cimag(rval))) {
goto fail;
}
}
// binary_search_with_guess 需要至少一个包含 3 个元素的数组
if (lenxp == 1) {
const npy_double xp_val = dx[0];
const npy_cdouble fp_val = dy[0];
NPY_BEGIN_THREADS_THRESHOLDED(lenx);
for (i = 0; i < lenx; ++i) {
const npy_double x_val = dz[i];
dres[i] = (x_val < xp_val) ? lval :
((x_val > xp_val) ? rval : fp_val);
}
NPY_END_THREADS;
}
else {
// 初始化变量 j 为 0
npy_intp j = 0;
/* only pre-calculate slopes if there are relatively few of them. */
// 仅在斜率相对较少时预先计算斜率
if (lenxp <= lenx) {
// 分配内存以存储斜率数组,长度为 (lenxp - 1)
slopes = PyArray_malloc((lenxp - 1) * sizeof(npy_cdouble));
// 如果内存分配失败,设置内存错误并跳转到失败标签
if (slopes == NULL) {
PyErr_NoMemory();
goto fail;
}
}
NPY_BEGIN_THREADS; // 开始线程安全操作
// 如果斜率数组非空,则计算斜率
if (slopes != NULL) {
for (i = 0; i < lenxp - 1; ++i) {
// 计算斜率的实部和虚部
const double inv_dx = 1.0 / (dx[i+1] - dx[i]);
npy_csetreal(&slopes[i], (npy_creal(dy[i+1]) - npy_creal(dy[i])) * inv_dx);
npy_csetimag(&slopes[i], (npy_cimag(dy[i+1]) - npy_cimag(dy[i])) * inv_dx);
}
}
// 遍历输入数组的元素
for (i = 0; i < lenx; ++i) {
// 获取当前输入值 x_val
const npy_double x_val = dz[i];
// 如果 x_val 是 NaN,则将结果设置为 x_val 的实部为 NaN,虚部为 0
if (npy_isnan(x_val)) {
npy_csetreal(&dres[i], x_val);
npy_csetimag(&dres[i], 0.0);
continue; // 继续下一个循环
}
// 使用二分搜索找到 x_val 在 dx 中的位置 j
j = binary_search_with_guess(x_val, dx, lenxp, j);
// 根据搜索的结果 j 进行插值操作或者直接返回边界值
if (j == -1) {
dres[i] = lval;
}
else if (j == lenxp) {
dres[i] = rval;
}
else if (j == lenxp - 1) {
dres[i] = dy[j];
}
else if (dx[j] == x_val) {
/* Avoid potential non-finite interpolation */
dres[i] = dy[j];
}
else {
npy_cdouble slope;
// 如果斜率数组非空,则使用预先计算的斜率
if (slopes != NULL) {
slope = slopes[j];
}
// 否则根据两个相邻点的差值计算斜率
else {
const npy_double inv_dx = 1.0 / (dx[j+1] - dx[j]);
npy_csetreal(&slope, (npy_creal(dy[j+1]) - npy_creal(dy[j])) * inv_dx);
npy_csetimag(&slope, (npy_cimag(dy[j+1]) - npy_cimag(dy[j])) * inv_dx);
}
// 进行线性插值计算,同时处理可能出现的 NaN
npy_csetreal(&dres[i], npy_creal(slope)*(x_val - dx[j]) + npy_creal(dy[j]));
if (NPY_UNLIKELY(npy_isnan(npy_creal(dres[i])))) {
npy_csetreal(&dres[i], npy_creal(slope)*(x_val - dx[j+1]) + npy_creal(dy[j+1]));
if (NPY_UNLIKELY(npy_isnan(npy_creal(dres[i]))) &&
npy_creal(dy[j]) == npy_creal(dy[j+1])) {
npy_csetreal(&dres[i], npy_creal(dy[j]));
}
}
npy_csetimag(&dres[i], npy_cimag(slope)*(x_val - dx[j]) + npy_cimag(dy[j]));
if (NPY_UNLIKELY(npy_isnan(npy_cimag(dres[i])))) {
npy_csetimag(&dres[i], npy_cimag(slope)*(x_val - dx[j+1]) + npy_cimag(dy[j+1]));
if (NPY_UNLIKELY(npy_isnan(npy_cimag(dres[i]))) &&
npy_cimag(dy[j]) == npy_cimag(dy[j+1])) {
npy_csetimag(&dres[i], npy_cimag(dy[j]));
}
}
}
}
NPY_END_THREADS; // 结束线程安全操作
}
PyArray_free(slopes);
Py_DECREF(afp);
Py_DECREF(axp);
Py_DECREF(ax);
return PyArray_Return(af);
/*
* 清理和释放资源,然后返回 NULL,表示失败
*/
fail:
Py_XDECREF(afp);
Py_XDECREF(axp);
Py_XDECREF(ax);
Py_XDECREF(af);
return NULL;
}
/*
* 空序列错误信息,用于指示非整数索引的情况
*/
static const char *EMPTY_SEQUENCE_ERR_MSG = "indices must be integral: the provided " \
"empty sequence was inferred as float. Wrap it with " \
"'np.array(indices, dtype=np.intp)'";
/*
* 非整数错误信息,仅允许整数索引
*/
static const char *NON_INTEGRAL_ERROR_MSG = "only int indices permitted";
/*
* 将 Python 对象 obj 转换为具有整数 dtype 的 ndarray,或者转换失败
*/
static PyArrayObject *
astype_anyint(PyObject *obj) {
PyArrayObject *ret;
if (!PyArray_Check(obj)) {
/* 首选 int dtype */
PyArray_Descr *dtype_guess = NULL;
if (PyArray_DTypeFromObject(obj, NPY_MAXDIMS, &dtype_guess) < 0) {
return NULL;
}
if (dtype_guess == NULL) {
if (PySequence_Check(obj) && PySequence_Size(obj) == 0) {
PyErr_SetString(PyExc_TypeError, EMPTY_SEQUENCE_ERR_MSG);
}
return NULL;
}
ret = (PyArrayObject*)PyArray_FromAny(obj, dtype_guess, 0, 0, 0, NULL);
if (ret == NULL) {
return NULL;
}
}
else {
ret = (PyArrayObject *)obj;
Py_INCREF(ret);
}
if (!(PyArray_ISINTEGER(ret) || PyArray_ISBOOL(ret))) {
/* 确保 dtype 是基于 int 的 */
PyErr_SetString(PyExc_TypeError, NON_INTEGRAL_ERROR_MSG);
Py_DECREF(ret);
return NULL;
}
return ret;
}
/*
* 将 Python 序列转换为 'count' 个 PyArrayObject 数组
*
* seq - 输入的 Python 对象,通常是一个元组,但任何序列都可以工作。
* 必须包含整数内容。
* paramname - 产生 'seq' 的参数名称。
* count - 应该有多少数组(如果不匹配则报错)。
* op - 数组的存放位置。
*/
static int int_sequence_to_arrays(PyObject *seq,
char *paramname,
int count,
PyArrayObject **op
)
{
int i;
if (!PySequence_Check(seq) || PySequence_Size(seq) != count) {
PyErr_Format(PyExc_ValueError,
"parameter %s must be a sequence of length %d",
paramname, count);
return -1;
}
for (i = 0; i < count; ++i) {
PyObject *item = PySequence_GetItem(seq, i);
if (item == NULL) {
goto fail;
}
op[i] = astype_anyint(item);
Py_DECREF(item);
if (op[i] == NULL) {
goto fail;
}
}
return 0;
fail:
while (--i >= 0) {
Py_XDECREF(op[i]);
op[i] = NULL;
}
return -1;
}
/*
* ravel_multi_index 的内部循环
*/
static int
ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims,
npy_intp *ravel_strides,
npy_intp count,
NPY_CLIPMODE *modes,
char **coords, npy_intp *coords_strides)
{
int i;
char invalid;
npy_intp j, m;
/*
* 检查是否存在零维的轴,除非没有需要处理的情况。
* 空数组/形状根本无法进行索引。
*/
if (count != 0) {
// 遍历展平后的维度数组
for (i = 0; i < ravel_ndim; ++i) {
// 如果有任何一个维度为0,抛出数值错误异常
if (ravel_dims[i] == 0) {
PyErr_SetString(PyExc_ValueError,
"cannot unravel if shape has zero entries (is empty).");
return NPY_FAIL;
}
}
}
// 允许线程进入临界区域
NPY_BEGIN_ALLOW_THREADS;
invalid = 0;
// 循环处理每个索引
while (count--) {
npy_intp raveled = 0;
// 遍历展平后的维度数组
for (i = 0; i < ravel_ndim; ++i) {
m = ravel_dims[i];
// 获取当前坐标的索引值
j = *(npy_intp *)coords[i];
switch (modes[i]) {
case NPY_RAISE:
// 如果索引超出了范围,设置无效标志并跳出循环
if (j < 0 || j >= m) {
invalid = 1;
goto end_while;
}
break;
case NPY_WRAP:
// 对超出范围的索引进行循环包裹
if (j < 0) {
j += m;
if (j < 0) {
j = j % m;
if (j != 0) {
j += m;
}
}
}
else if (j >= m) {
j -= m;
if (j >= m) {
j = j % m;
}
}
break;
case NPY_CLIP:
// 对超出范围的索引进行截断处理
if (j < 0) {
j = 0;
}
else if (j >= m) {
j = m - 1;
}
break;
}
// 计算展平后的索引值
raveled += j * ravel_strides[i];
coords[i] += coords_strides[i];
}
// 将展平后的索引值写入坐标数组
*(npy_intp *)coords[ravel_ndim] = raveled;
coords[ravel_ndim] += coords_strides[ravel_ndim];
}
end_while:
NPY_END_ALLOW_THREADS;
// 结束线程安全区域
if (invalid) {
// 如果坐标数组中存在无效项,设置值错误异常并返回失败状态
PyErr_SetString(PyExc_ValueError,
"invalid entry in coordinates array");
return NPY_FAIL;
}
// 返回成功状态
return NPY_SUCCEED;
}
/* ravel_multi_index implementation - see add_newdocs.py */
NPY_NO_EXPORT PyObject *
arr_ravel_multi_index(PyObject *self, PyObject *args, PyObject *kwds)
{
int i;
PyObject *mode0=NULL, *coords0=NULL;
PyArrayObject *ret = NULL;
PyArray_Dims dimensions={0,0};
npy_intp s, ravel_strides[NPY_MAXDIMS];
NPY_ORDER order = NPY_CORDER;
NPY_CLIPMODE modes[NPY_MAXDIMS];
PyArrayObject *op[NPY_MAXARGS];
PyArray_Descr *dtype[NPY_MAXARGS];
npy_uint32 op_flags[NPY_MAXARGS];
NpyIter *iter = NULL;
static char *kwlist[] = {"multi_index", "dims", "mode", "order", NULL};
// 初始化操作数组和数据类型数组
memset(op, 0, sizeof(op));
dtype[0] = NULL;
// 解析参数和关键字参数
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"OO&|OO&:ravel_multi_index", kwlist,
&coords0,
PyArray_IntpConverter, &dimensions,
&mode0,
PyArray_OrderConverter, &order)) {
goto fail;
}
// 检查维度数量是否超过最大值
if (dimensions.len+1 > NPY_MAXARGS) {
PyErr_SetString(PyExc_ValueError,
"too many dimensions passed to ravel_multi_index");
goto fail;
}
// 转换并检查剪切模式序列
if (!PyArray_ConvertClipmodeSequence(mode0, modes, dimensions.len)) {
goto fail;
}
// 根据顺序计算展平步长
switch (order) {
case NPY_CORDER:
s = 1;
for (i = dimensions.len-1; i >= 0; --i) {
ravel_strides[i] = s;
if (npy_mul_sizes_with_overflow(&s, s, dimensions.ptr[i])) {
PyErr_SetString(PyExc_ValueError,
"invalid dims: array size defined by dims is larger "
"than the maximum possible size.");
goto fail;
}
}
break;
case NPY_FORTRANORDER:
s = 1;
for (i = 0; i < dimensions.len; ++i) {
ravel_strides[i] = s;
if (npy_mul_sizes_with_overflow(&s, s, dimensions.ptr[i])) {
PyErr_SetString(PyExc_ValueError,
"invalid dims: array size defined by dims is larger "
"than the maximum possible size.");
goto fail;
}
}
break;
default:
PyErr_SetString(PyExc_ValueError,
"only 'C' or 'F' order is permitted");
goto fail;
}
// 将多索引转换为操作数组
if (int_sequence_to_arrays(coords0, "multi_index", dimensions.len, op) < 0) {
goto fail;
}
// 设置操作数组的标志
for (i = 0; i < dimensions.len; ++i) {
op_flags[i] = NPY_ITER_READONLY|
NPY_ITER_ALIGNED;
}
op_flags[dimensions.len] = NPY_ITER_WRITEONLY|
NPY_ITER_ALIGNED|
NPY_ITER_ALLOCATE;
dtype[0] = PyArray_DescrFromType(NPY_INTP);
for (i = 1; i <= dimensions.len; ++i) {
dtype[i] = dtype[0];
}
iter = NpyIter_MultiNew(dimensions.len+1, op, NPY_ITER_BUFFERED|
NPY_ITER_EXTERNAL_LOOP|
NPY_ITER_ZEROSIZE_OK,
NPY_KEEPORDER,
NPY_SAME_KIND_CASTING,
op_flags, dtype);
if (iter == NULL) {
goto fail;
}
if (NpyIter_GetIterSize(iter) != 0) {
NpyIter_IterNextFunc *iternext;
char **dataptr;
npy_intp *strides;
npy_intp *countptr;
iternext = NpyIter_GetIterNext(iter, NULL);
if (iternext == NULL) {
goto fail;
}
dataptr = NpyIter_GetDataPtrArray(iter);
strides = NpyIter_GetInnerStrideArray(iter);
countptr = NpyIter_GetInnerLoopSizePtr(iter);
do {
if (ravel_multi_index_loop(dimensions.len, dimensions.ptr,
ravel_strides, *countptr, modes,
dataptr, strides) != NPY_SUCCEED) {
goto fail;
}
} while(iternext(iter));
}
ret = NpyIter_GetOperandArray(iter)[dimensions.len];
Py_INCREF(ret);
Py_DECREF(dtype[0]);
for (i = 0; i < dimensions.len; ++i) {
Py_XDECREF(op[i]);
}
npy_free_cache_dim_obj(dimensions);
NpyIter_Deallocate(iter);
return PyArray_Return(ret);
fail:
// 释放 dtype[0] 指向的对象,减少其引用计数
Py_XDECREF(dtype[0]);
// 循环释放 op 数组中每个元素指向的对象,减少它们的引用计数
for (i = 0; i < dimensions.len; ++i) {
Py_XDECREF(op[i]);
}
// 释放 dimensions 所占用的内存
npy_free_cache_dim_obj(dimensions);
// 释放 NpyIter 迭代器所占用的资源
NpyIter_Deallocate(iter);
// 返回 NULL,表示操作失败
return NULL;
}
/*
* Inner loop for unravel_index
* order must be NPY_CORDER or NPY_FORTRANORDER
*/
static int
unravel_index_loop(int unravel_ndim, npy_intp const *unravel_dims,
npy_intp unravel_size, npy_intp count,
char *indices, npy_intp indices_stride,
npy_intp *coords, NPY_ORDER order)
{
int i, idx;
// 根据 order 设置起始索引位置和步长
int idx_start = (order == NPY_CORDER) ? unravel_ndim - 1: 0;
int idx_step = (order == NPY_CORDER) ? -1 : 1;
char invalid = 0;
npy_intp val = 0;
NPY_BEGIN_ALLOW_THREADS;
// 断言 order 必须是 NPY_CORDER 或 NPY_FORTRANORDER
assert(order == NPY_CORDER || order == NPY_FORTRANORDER);
while (count--) {
// 从 indices 中读取当前的 val 值
val = *(npy_intp *)indices;
// 检查 val 是否在合法范围内
if (val < 0 || val >= unravel_size) {
invalid = 1;
break;
}
idx = idx_start;
for (i = 0; i < unravel_ndim; ++i) {
/*
* 使用一个局部变量可能启用单一的除法优化
* 但是只有在 / 操作符在 % 操作符之前时才会生效
*/
npy_intp tmp = val / unravel_dims[idx];
// 计算当前坐标的值,并更新 coords 数组
coords[idx] = val % unravel_dims[idx];
// 更新 val 为下一个维度的坐标值
val = tmp;
// 根据步长更新 idx 索引
idx += idx_step;
}
// 更新 coords 和 indices 的指针位置
coords += unravel_ndim;
indices += indices_stride;
}
NPY_END_ALLOW_THREADS;
// 如果发现索引超出范围,抛出异常并返回失败标志
if (invalid) {
PyErr_Format(PyExc_ValueError,
"index %" NPY_INTP_FMT " is out of bounds for array with size "
"%" NPY_INTP_FMT,
val, unravel_size
);
return NPY_FAIL;
}
// 操作成功,返回成功标志
return NPY_SUCCEED;
}
/* unravel_index implementation - see add_newdocs.py */
NPY_NO_EXPORT PyObject *
arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *indices0 = NULL;
PyObject *ret_tuple = NULL;
PyArrayObject *ret_arr = NULL;
PyArrayObject *indices = NULL;
PyArray_Descr *dtype = NULL;
PyArray_Dims dimensions = {0, 0};
NPY_ORDER order = NPY_CORDER;
npy_intp unravel_size;
NpyIter *iter = NULL;
int i, ret_ndim;
npy_intp ret_dims[NPY_MAXDIMS], ret_strides[NPY_MAXDIMS];
static char *kwlist[] = {"indices", "shape", "order", NULL};
// 解析输入参数,获取 indices、dimensions 和 order
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&|O&:unravel_index",
kwlist,
&indices0,
PyArray_IntpConverter, &dimensions,
PyArray_OrderConverter, &order)) {
// 解析失败,跳转到 fail 标签处
goto fail;
}
// 计算 unravel_size,即 dimensions 中所有元素的乘积
unravel_size = PyArray_OverflowMultiplyList(dimensions.ptr, dimensions.len);
// 检查是否计算溢出
if (unravel_size == -1) {
// 如果溢出,抛出 ValueError 异常并跳转到 fail 标签处
PyErr_SetString(PyExc_ValueError,
"dimensions are too large; arrays and shapes with "
"a total size greater than 'intp' are not supported.");
goto fail;
}
indices = astype_anyint(indices0);
if (indices == NULL) {
goto fail;
}
dtype = PyArray_DescrFromType(NPY_INTP);
if (dtype == NULL) {
goto fail;
}
iter = NpyIter_New(indices, NPY_ITER_READONLY|
NPY_ITER_ALIGNED|
NPY_ITER_BUFFERED|
NPY_ITER_ZEROSIZE_OK|
NPY_ITER_DONT_NEGATE_STRIDES|
NPY_ITER_MULTI_INDEX,
NPY_KEEPORDER, NPY_SAME_KIND_CASTING,
dtype);
if (iter == NULL) {
goto fail;
}
/*
* Create the return array with a layout compatible with the indices
* and with a dimension added to the end for the multi-index
*/
// 创建一个返回数组,其布局与indices兼容,并在末尾增加一个维度用于多索引
ret_ndim = PyArray_NDIM(indices) + 1;
if (NpyIter_GetShape(iter, ret_dims) != NPY_SUCCEED) {
goto fail;
}
// 获取返回数组的维度,并设置最后一个维度为dimensions.len
ret_dims[ret_ndim-1] = dimensions.len;
if (NpyIter_CreateCompatibleStrides(iter,
dimensions.len*sizeof(npy_intp), ret_strides) != NPY_SUCCEED) {
goto fail;
}
// 创建与迭代器兼容的步幅,并设置最后一个维度的步幅为sizeof(npy_intp)
/* Remove the multi-index and inner loop */
// 移除多索引和内部循环
if (NpyIter_RemoveMultiIndex(iter) != NPY_SUCCEED) {
goto fail;
}
// 移除迭代器的多索引功能
if (NpyIter_EnableExternalLoop(iter) != NPY_SUCCEED) {
goto fail;
}
// 启用外部循环功能
ret_arr = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype,
ret_ndim, ret_dims, ret_strides, NULL, 0, NULL);
// 使用描述器创建一个新的PyArrayObject作为返回数组
dtype = NULL;
if (ret_arr == NULL) {
goto fail;
}
if (order != NPY_CORDER && order != NPY_FORTRANORDER) {
PyErr_SetString(PyExc_ValueError,
"only 'C' or 'F' order is permitted");
goto fail;
}
// 检查order是否为NPY_CORDER或NPY_FORTRANORDER,否则设置错误并跳转到fail标签
if (NpyIter_GetIterSize(iter) != 0) {
NpyIter_IterNextFunc *iternext;
char **dataptr;
npy_intp *strides;
npy_intp *countptr, count;
npy_intp *coordsptr = (npy_intp *)PyArray_DATA(ret_arr);
iternext = NpyIter_GetIterNext(iter, NULL);
// 获取迭代器的下一个函数指针
if (iternext == NULL) {
goto fail;
}
// 如果获取失败则跳转到fail标签
dataptr = NpyIter_GetDataPtrArray(iter);
// 获取迭代器中数据指针的数组
strides = NpyIter_GetInnerStrideArray(iter);
// 获取迭代器中内部步幅的数组
countptr = NpyIter_GetInnerLoopSizePtr(iter);
// 获取迭代器中内部循环大小的指针
do {
count = *countptr;
// 获取当前内部循环的大小
if (unravel_index_loop(dimensions.len, dimensions.ptr,
unravel_size, count, *dataptr, *strides,
coordsptr, order) != NPY_SUCCEED) {
goto fail;
}
// 对当前内部循环进行展开索引操作,如果失败则跳转到fail标签
coordsptr += count * dimensions.len;
// 更新coordsptr,移动到下一个内部循环的开始位置
} while (iternext(iter));
// 使用iternext函数进行迭代,直到迭代结束
}
/*
* 如果 dimensions.len 为 0 且 indices 的维度不为 0,
* 表示对于零维数组没有索引意义上的“取唯一元素十次”操作,
* 因此我们别无选择只能报错。(参见 gh-580)
*
* 在迭代完成后进行这个检查,这样可以为无效索引提供更好的错误消息。
*/
if (dimensions.len == 0 && PyArray_NDIM(indices) != 0) {
PyErr_SetString(PyExc_ValueError,
"multiple indices are not supported for 0d arrays");
goto fail;
}
/* 现在根据每个索引创建视图的元组 */
ret_tuple = PyTuple_New(dimensions.len);
if (ret_tuple == NULL) {
goto fail;
}
for (i = 0; i < dimensions.len; ++i) {
PyArrayObject *view;
view = (PyArrayObject *)PyArray_NewFromDescrAndBase(
&PyArray_Type, PyArray_DescrFromType(NPY_INTP),
ret_ndim - 1, ret_dims, ret_strides,
PyArray_BYTES(ret_arr) + i*sizeof(npy_intp),
NPY_ARRAY_WRITEABLE, NULL, (PyObject *)ret_arr);
if (view == NULL) {
goto fail;
}
PyTuple_SET_ITEM(ret_tuple, i, PyArray_Return(view));
}
Py_DECREF(ret_arr);
Py_XDECREF(indices);
npy_free_cache_dim_obj(dimensions);
NpyIter_Deallocate(iter);
// 返回创建的视图元组
return ret_tuple;
fail:
// 释放 ret_tuple 所引用的 Python 对象,减少其引用计数
Py_XDECREF(ret_tuple);
// 释放 ret_arr 所引用的 Python 对象,减少其引用计数
Py_XDECREF(ret_arr);
// 释放 dtype 所引用的 Python 对象,减少其引用计数
Py_XDECREF(dtype);
// 释放 indices 所引用的 Python 对象,减少其引用计数
Py_XDECREF(indices);
// 释放之前分配的维度缓存对象
npy_free_cache_dim_obj(dimensions);
// 释放 NpyIter 对象及其内部资源
NpyIter_Deallocate(iter);
// 返回 NULL 指针,表示失败
return NULL;
}
/* Can only be called if doc is currently NULL */
NPY_NO_EXPORT PyObject *
arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t len_args)
{
PyObject *obj;
PyObject *str;
const char *docstr;
static char *msg = "already has a different docstring";
/* Don't add docstrings */
// 如果 Python 解释器版本支持静态优化并且已启用,直接返回 None
#if PY_VERSION_HEX > 0x030b0000
if (npy_static_cdata.optimize > 1) {
#else
if (Py_OptimizeFlag > 1) {
#endif
Py_RETURN_NONE;
}
// 准备解析参数
NPY_PREPARE_ARGPARSER;
// 解析参数,期望两个参数:一个对象和一个字符串
if (npy_parse_arguments("add_docstring", args, len_args, NULL,
"", NULL, &obj,
"", NULL, &str,
NULL, NULL, NULL) < 0) {
return NULL;
}
// 确保传入的字符串参数是一个 Unicode 对象
if (!PyUnicode_Check(str)) {
PyErr_SetString(PyExc_TypeError,
"argument docstring of add_docstring should be a str");
return NULL;
}
// 将 Unicode 字符串转换为 UTF-8 格式的 C 字符串
docstr = PyUnicode_AsUTF8(str);
if (docstr == NULL) {
return NULL;
}
// 定义宏 _ADDDOC,用于设置对象的文档字符串
#define _ADDDOC(doc, name) \
if (!(doc)) { \
doc = docstr; \
Py_INCREF(str); /* hold on to string (leaks reference) */ \
} \
else if (strcmp(doc, docstr) != 0) { \
PyErr_Format(PyExc_RuntimeError, "%s method %s", name, msg); \
return NULL; \
}
// 根据对象类型设置相应的文档字符串
if (Py_TYPE(obj) == &PyCFunction_Type) {
// 如果是 C 函数对象,则设置其 ml_doc 字段为传入的文档字符串
PyCFunctionObject *new = (PyCFunctionObject *)obj;
_ADDDOC(new->m_ml->ml_doc, new->m_ml->ml_name);
}
else if (PyObject_TypeCheck(obj, &PyType_Type)) {
// 如果是 Python 类型对象,则设置其 tp_doc 字段和 __doc__ 字典项为传入的文档字符串
PyTypeObject *new = (PyTypeObject *)obj;
_ADDDOC(new->tp_doc, new->tp_name);
// 如果 tp_dict 不为空且 __doc__ 项为 None,则替换为传入的文档字符串
if (new->tp_dict != NULL && PyDict_CheckExact(new->tp_dict) &&
PyDict_GetItemString(new->tp_dict, "__doc__") == Py_None) {
// 警告:修改 tp_dict 不一定安全!
if (PyDict_SetItemString(new->tp_dict, "__doc__", str) < 0) {
return NULL;
}
}
}
# 如果对象的类型是 PyMemberDescr_Type
else if (Py_TYPE(obj) == &PyMemberDescr_Type) {
# 将 obj 转换为 PyMemberDescrObject 类型
PyMemberDescrObject *new = (PyMemberDescrObject *)obj;
# 调用 _ADDDOC 函数,将 new 对象的成员变量文档和名称传入
_ADDDOC(new->d_member->doc, new->d_member->name);
}
# 如果对象的类型是 PyGetSetDescr_Type
else if (Py_TYPE(obj) == &PyGetSetDescr_Type) {
# 将 obj 转换为 PyGetSetDescrObject 类型
PyGetSetDescrObject *new = (PyGetSetDescrObject *)obj;
# 调用 _ADDDOC 函数,将 new 对象的获取设置文档和名称传入
_ADDDOC(new->d_getset->doc, new->d_getset->name);
}
# 如果对象的类型是 PyMethodDescr_Type
else if (Py_TYPE(obj) == &PyMethodDescr_Type) {
# 将 obj 转换为 PyMethodDescrObject 类型
PyMethodDescrObject *new = (PyMethodDescrObject *)obj;
# 调用 _ADDDOC 函数,将 new 对象的方法文档和名称传入
_ADDDOC(new->d_method->ml_doc, new->d_method->ml_name);
}
# 如果对象不属于上述三种类型
else {
PyObject *doc_attr;
# 获取对象的 "__doc__" 属性
doc_attr = PyObject_GetAttrString(obj, "__doc__");
# 如果获取成功且不为 None,并且对象的 __doc__ 属性与 str 不相等
if (doc_attr != NULL && doc_attr != Py_None &&
(PyUnicode_Compare(doc_attr, str) != 0)) {
Py_DECREF(doc_attr);
# 如果在比较时发生错误
if (PyErr_Occurred()) {
/* error during PyUnicode_Compare */
return NULL;
}
# 抛出运行时错误,指明对象的信息
PyErr_Format(PyExc_RuntimeError, "object %s", msg);
return NULL;
}
Py_XDECREF(doc_attr);
# 将对象的 "__doc__" 属性设置为 str
if (PyObject_SetAttrString(obj, "__doc__", str) < 0) {
# 如果设置失败,抛出类型错误
PyErr_SetString(PyExc_TypeError,
"Cannot set a docstring for that object");
return NULL;
}
# 返回 None
Py_RETURN_NONE;
}
/*
* 返回一个 Python None 对象,表示函数执行完毕没有返回值。
*/
Py_RETURN_NONE;
}
/*
* 此函数将输入数组中的布尔值打包到字节数组的位中。布尔真值按常规方式确定:0为假,其他值为真。
*/
static NPY_GCC_OPT_3 inline void
pack_inner(const char *inptr,
npy_intp element_size, /* 每个元素的大小,以字节为单位 */
npy_intp n_in, /* 输入数组中的元素数量 */
npy_intp in_stride, /* 输入数组的步幅 */
char *outptr,
npy_intp n_out, /* 输出数组的元素数量 */
npy_intp out_stride, /* 输出数组的步幅 */
PACK_ORDER order) /* 打包顺序 */
{
/*
* 遍历 inptr 的元素。
* 确定它是否为非零值。
* 是:设置对应的位(并调整构建值)
* 否:继续下一个元素
* 每8个值,设置构建值并递增 outptr
*/
npy_intp index = 0;
int remain = n_in % 8; /* 不均匀的位数 */
#if NPY_SIMD
// 检查条件:输入步长为1、元素大小为1字节、输出数量大于2时进入条件块
if (in_stride == 1 && element_size == 1 && n_out > 2) {
// 创建全零的8位整数向量
npyv_u8 v_zero = npyv_zero_u8();
/* 不处理非完整的8字节余数 */
// 计算有效输出数量,排除余数的影响
npy_intp vn_out = n_out - (remain ? 1 : 0);
// 设置向量步长为64位
const int vstep = npyv_nlanes_u64;
// 设置4倍向量步长
const int vstepx4 = vstep * 4;
// 检查输出指针是否按64位对齐
const int isAligned = npy_is_aligned(outptr, sizeof(npy_uint64));
// 调整有效输出数量,使其按向量步长对齐
vn_out -= (vn_out & (vstep - 1));
// 循环处理主体,以向量步长x4为单位处理数据
for (; index <= vn_out - vstepx4; index += vstepx4, inptr += npyv_nlanes_u8 * 4) {
// 加载4个8位整数向量
npyv_u8 v0 = npyv_load_u8((const npy_uint8*)inptr);
npyv_u8 v1 = npyv_load_u8((const npy_uint8*)inptr + npyv_nlanes_u8 * 1);
npyv_u8 v2 = npyv_load_u8((const npy_uint8*)inptr + npyv_nlanes_u8 * 2);
npyv_u8 v3 = npyv_load_u8((const npy_uint8*)inptr + npyv_nlanes_u8 * 3);
// 如果顺序为大端,反转每个8位整数向量
if (order == PACK_ORDER_BIG) {
v0 = npyv_rev64_u8(v0);
v1 = npyv_rev64_u8(v1);
v2 = npyv_rev64_u8(v2);
v3 = npyv_rev64_u8(v3);
}
// 定义一个64位整数数组
npy_uint64 bb[4];
// 将每个8位整数向量转换为比特表示,并存储到数组中
bb[0] = npyv_tobits_b8(npyv_cmpneq_u8(v0, v_zero));
bb[1] = npyv_tobits_b8(npyv_cmpneq_u8(v1, v_zero));
bb[2] = npyv_tobits_b8(npyv_cmpneq_u8(v2, v_zero));
bb[3] = npyv_tobits_b8(npyv_cmpneq_u8(v3, v_zero));
// 如果输出步长为1且满足对齐要求或者不需要对齐
if(out_stride == 1 &&
(!NPY_ALIGNMENT_REQUIRED || isAligned)) {
// 强制类型转换为64位整数指针
npy_uint64 *ptr64 = (npy_uint64*)outptr;
// 根据 SIMD 宽度执行不同的位运算和存储操作
#if NPY_SIMD_WIDTH == 16
npy_uint64 bcomp = bb[0] | (bb[1] << 16) | (bb[2] << 32) | (bb[3] << 48);
ptr64[0] = bcomp;
#elif NPY_SIMD_WIDTH == 32
ptr64[0] = bb[0] | (bb[1] << 32);
ptr64[1] = bb[2] | (bb[3] << 32);
#else
ptr64[0] = bb[0]; ptr64[1] = bb[1];
ptr64[2] = bb[2]; ptr64[3] = bb[3];
#endif
// 更新输出指针位置
outptr += vstepx4;
} else {
// 如果输出步长不为1或者需要对齐,逐位复制数据到输出指针
for(int i = 0; i < 4; i++) {
for (int j = 0; j < vstep; j++) {
memcpy(outptr, (char*)&bb[i] + j, 1);
outptr += out_stride;
}
}
}
}
// 处理剩余的向量元素,不足一组的部分
for (; index < vn_out; index += vstep, inptr += npyv_nlanes_u8) {
// 加载一个8位整数向量
npyv_u8 va = npyv_load_u8((const npy_uint8*)inptr);
// 如果顺序为大端,反转该8位整数向量
if (order == PACK_ORDER_BIG) {
va = npyv_rev64_u8(va);
}
// 将该8位整数向量转换为比特表示
npy_uint64 bb = npyv_tobits_b8(npyv_cmpneq_u8(va, v_zero));
// 逐位复制数据到输出指针
for (int i = 0; i < vstep; ++i) {
memcpy(outptr, (char*)&bb + i, 1);
outptr += out_stride;
}
}
}
#endif
if (remain == 0) { /* assumes n_in > 0 */
remain = 8;
}
/* 不重置索引,只处理上面代码块的剩余部分 */
for (; index < n_out; index++) {
unsigned char build = 0;
int maxi = (index == n_out - 1) ? remain : 8;
if (order == PACK_ORDER_BIG) {
// 大端序
for (int i = 0; i < maxi; i++) {
build <<= 1;
for (npy_intp j = 0; j < element_size; j++) {
build |= (inptr[j] != 0);
}
inptr += in_stride;
}
if (index == n_out - 1) {
build <<= 8 - remain;
}
}
else
{
// 小端序
for (int i = 0; i < maxi; i++) {
build >>= 1;
for (npy_intp j = 0; j < element_size; j++) {
build |= (inptr[j] != 0) ? 128 : 0;
}
inptr += in_stride;
}
if (index == n_out - 1) {
build >>= 8 - remain;
}
}
*outptr = (char)build;
outptr += out_stride;
}
}
static PyObject *
pack_bits(PyObject *input, int axis, char order)
{
PyArrayObject *inp;
PyArrayObject *new = NULL;
PyArrayObject *out = NULL;
npy_intp outdims[NPY_MAXDIMS];
int i;
PyArrayIterObject *it, *ot;
NPY_BEGIN_THREADS_DEF;
inp = (PyArrayObject *)PyArray_FROM_O(input);
if (inp == NULL) {
return NULL;
}
if (!PyArray_ISBOOL(inp) && !PyArray_ISINTEGER(inp)) {
PyErr_SetString(PyExc_TypeError,
"Expected an input array of integer or boolean data type");
Py_DECREF(inp);
goto fail;
}
new = (PyArrayObject *)PyArray_CheckAxis(inp, &axis, 0);
Py_DECREF(inp);
if (new == NULL) {
return NULL;
}
if (PyArray_NDIM(new) == 0) {
char *optr, *iptr;
out = (PyArrayObject *)PyArray_NewFromDescr(
Py_TYPE(new), PyArray_DescrFromType(NPY_UBYTE),
0, NULL, NULL, NULL,
0, NULL);
if (out == NULL) {
goto fail;
}
optr = PyArray_DATA(out);
iptr = PyArray_DATA(new);
*optr = 0;
for (i = 0; i < PyArray_ITEMSIZE(new); i++) {
if (*iptr != 0) {
*optr = 1;
break;
}
iptr++;
}
goto finish;
}
/* 设置输出形状 */
for (i = 0; i < PyArray_NDIM(new); i++) {
outdims[i] = PyArray_DIM(new, i);
}
/*
* 将轴的维度除以8
* 8 -> 1, 9 -> 2, 16 -> 2, 17 -> 3 等等..
*/
outdims[axis] = ((outdims[axis] - 1) >> 3) + 1;
/* 创建输出数组 */
out = (PyArrayObject *)PyArray_NewFromDescr(
Py_TYPE(new), PyArray_DescrFromType(NPY_UBYTE),
PyArray_NDIM(new), outdims, NULL, NULL,
PyArray_ISFORTRAN(new), NULL);
if (out == NULL) {
goto fail;
}
# 设置迭代器以便在除了给定轴以外的所有维度上进行迭代
it = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)new, &axis);
ot = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)out, &axis);
# 如果迭代器初始化失败,则释放资源并跳转到错误处理部分
if (it == NULL || ot == NULL) {
Py_XDECREF(it);
Py_XDECREF(ot);
goto fail;
}
# 根据给定的字节序列创建一个枚举类型,'b'表示大端序,'l'表示小端序
const PACK_ORDER ordere = order == 'b' ? PACK_ORDER_BIG : PACK_ORDER_LITTLE;
# 多线程处理的起始点,根据输出数组在给定轴上的维度进行决定
NPY_BEGIN_THREADS_THRESHOLDED(PyArray_DIM(out, axis));
# 迭代处理数组中的每个元素
while (PyArray_ITER_NOTDONE(it)) {
# 调用内部函数,对数组中的数据进行打包处理
pack_inner(PyArray_ITER_DATA(it), PyArray_ITEMSIZE(new),
PyArray_DIM(new, axis), PyArray_STRIDE(new, axis),
PyArray_ITER_DATA(ot), PyArray_DIM(out, axis),
PyArray_STRIDE(out, axis), ordere);
# 移动到下一个元素
PyArray_ITER_NEXT(it);
PyArray_ITER_NEXT(ot);
}
# 多线程处理结束
NPY_END_THREADS;
# 释放迭代器对象
Py_DECREF(it);
Py_DECREF(ot);
// 释放新建对象的 Python 引用计数,避免内存泄漏
Py_DECREF(new);
// 返回输出对象的 PyObject 指针类型,完成函数执行
return (PyObject *)out;
fail:
// 出错处理:释放新建对象的 Python 引用计数
Py_XDECREF(new);
// 出错处理:释放输出对象的 Python 引用计数
Py_XDECREF(out);
// 出错处理:返回空指针表示函数执行失败
return NULL;
}
static PyObject *
unpack_bits(PyObject *input, int axis, PyObject *count_obj, char order)
{
PyArrayObject *inp;
PyArrayObject *new = NULL;
PyArrayObject *out = NULL;
npy_intp outdims[NPY_MAXDIMS];
int i;
PyArrayIterObject *it, *ot;
npy_intp count, in_n, in_tail, out_pad, in_stride, out_stride;
NPY_BEGIN_THREADS_DEF;
// 从 Python 对象创建 PyArrayObject,转换为 NumPy 数组
inp = (PyArrayObject *)PyArray_FROM_O(input);
if (inp == NULL) {
// 如果转换失败,返回空指针表示函数执行失败
return NULL;
}
if (PyArray_TYPE(inp) != NPY_UBYTE) {
// 如果输入数组不是无符号字节类型,设置错误信息并释放输入数组
PyErr_SetString(PyExc_TypeError,
"Expected an input array of unsigned byte data type");
Py_DECREF(inp);
// 转到出错处理标签
goto fail;
}
// 检查轴的有效性,并返回新的 PyArrayObject
new = (PyArrayObject *)PyArray_CheckAxis(inp, &axis, 0);
Py_DECREF(inp);
if (new == NULL) {
// 如果检查轴失败,返回空指针表示函数执行失败
return NULL;
}
if (PyArray_NDIM(new) == 0) {
// 处理0维数组,将其转换为1维数组
PyArrayObject *temp;
PyArray_Dims newdim = {NULL, 1};
npy_intp shape = 1;
newdim.ptr = &shape;
// 创建新形状的数组,并释放原数组
temp = (PyArrayObject *)PyArray_Newshape(new, &newdim, NPY_CORDER);
Py_DECREF(new);
if (temp == NULL) {
// 如果创建新形状数组失败,返回空指针表示函数执行失败
return NULL;
}
new = temp;
}
// 设置输出数组的形状
for (i = 0; i < PyArray_NDIM(new); i++) {
outdims[i] = PyArray_DIM(new, i);
}
// 将指定轴的维度乘以8
outdims[axis] *= 8;
if (count_obj != Py_None) {
// 如果 count_obj 不是 None,将其转换为整数并检查
count = PyArray_PyIntAsIntp(count_obj);
if (error_converting(count)) {
// 如果转换失败,转到出错处理标签
goto fail;
}
if (count < 0) {
// 如果 count 是负数,调整输出数组的维度
outdims[axis] += count;
if (outdims[axis] < 0) {
// 如果调整后的维度小于0,设置错误信息并转到出错处理标签
PyErr_Format(PyExc_ValueError,
"-count larger than number of elements");
goto fail;
}
}
else {
// 如果 count 是非负数,设置输出数组的维度为 count
outdims[axis] = count;
}
}
// 创建输出数组
out = (PyArrayObject *)PyArray_NewFromDescr(
Py_TYPE(new), PyArray_DescrFromType(NPY_UBYTE),
PyArray_NDIM(new), outdims, NULL, NULL,
PyArray_ISFORTRAN(new), NULL);
if (out == NULL) {
// 如果创建输出数组失败,转到出错处理标签
goto fail;
}
// 设置迭代器,用于遍历除指定轴以外的所有维度
it = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)new, &axis);
ot = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)out, &axis);
if (it == NULL || ot == NULL) {
// 如果设置迭代器失败,释放迭代器并转到出错处理标签
Py_XDECREF(it);
Py_XDECREF(ot);
goto fail;
}
// 计算输入数据的位数和填充要求
count = PyArray_DIM(new, axis) * 8;
if (outdims[axis] > count) {
in_n = count / 8;
in_tail = 0;
out_pad = outdims[axis] - count;
}
else {
in_n = outdims[axis] / 8;
in_tail = outdims[axis] % 8;
out_pad = 0;
}
// 计算输入和输出数组的步幅
in_stride = PyArray_STRIDE(new, axis);
out_stride = PyArray_STRIDE(out, axis);
# 根据输出数组的大小设置线程阈值,用于多线程处理
NPY_BEGIN_THREADS_THRESHOLDED(PyArray_Size((PyObject *)out) / 8);
# 迭代器循环,直到迭代结束
while (PyArray_ITER_NOTDONE(it)) {
npy_intp index;
# 获取输入迭代器的当前数据指针
unsigned const char *inptr = PyArray_ITER_DATA(it);
# 获取输出迭代器的当前数据指针
char *outptr = PyArray_ITER_DATA(ot);
# 如果输出步长为1
if (out_stride == 1) {
/* 对于单位步长,可以直接从查找表中复制数据 */
if (order == 'b') {
# 按字节顺序(大端)循环处理输入数据
for (index = 0; index < in_n; index++) {
# 从静态数据结构中查找并获取64位整数,复制到输出指针位置
npy_uint64 v = npy_static_cdata.unpack_lookup_big[*inptr].uint64;
memcpy(outptr, &v, 8);
outptr += 8;
inptr += in_stride;
}
}
else {
# 按字节顺序(小端或者未指定顺序)循环处理输入数据
for (index = 0; index < in_n; index++) {
npy_uint64 v = npy_static_cdata.unpack_lookup_big[*inptr].uint64;
# 如果顺序不是大端,进行字节交换
if (order != 'b') {
v = npy_bswap8(v);
}
memcpy(outptr, &v, 8);
outptr += 8;
inptr += in_stride;
}
}
/* 清理尾部剩余部分 */
if (in_tail) {
npy_uint64 v = npy_static_cdata.unpack_lookup_big[*inptr].uint64;
if (order != 'b') {
v = npy_bswap8(v);
}
memcpy(outptr, &v, in_tail);
}
/* 添加填充 */
else if (out_pad) {
memset(outptr, 0, out_pad);
}
}
else {
# 如果输出步长不为1
if (order == 'b') {
# 按字节顺序(大端)循环处理输入数据
for (index = 0; index < in_n; index++) {
# 每个字节内的位处理
for (i = 0; i < 8; i++) {
*outptr = ((*inptr & (128 >> i)) != 0);
outptr += out_stride;
}
inptr += in_stride;
}
/* 清理尾部剩余部分 */
for (i = 0; i < in_tail; i++) {
*outptr = ((*inptr & (128 >> i)) != 0);
outptr += out_stride;
}
}
else {
# 按字节顺序(小端或者未指定顺序)循环处理输入数据
for (index = 0; index < in_n; index++) {
# 每个字节内的位处理
for (i = 0; i < 8; i++) {
*outptr = ((*inptr & (1 << i)) != 0);
outptr += out_stride;
}
inptr += in_stride;
}
/* 清理尾部剩余部分 */
for (i = 0; i < in_tail; i++) {
*outptr = ((*inptr & (1 << i)) != 0);
outptr += out_stride;
}
}
/* 添加填充 */
for (index = 0; index < out_pad; index++) {
*outptr = 0;
outptr += out_stride;
}
}
# 移动输入和输出迭代器到下一个位置
PyArray_ITER_NEXT(it);
PyArray_ITER_NEXT(ot);
}
# 结束多线程处理
NPY_END_THREADS;
# 释放迭代器对象
Py_DECREF(it);
Py_DECREF(ot);
# 释放新创建的数组对象
Py_DECREF(new);
# 返回输出数组对象
return (PyObject *)out;
fail:
// 减少 new 指针的引用计数,处理异常情况
Py_XDECREF(new);
// 减少 out 指针的引用计数,处理异常情况
Py_XDECREF(out);
// 返回空指针,表示函数执行失败
return NULL;
}
NPY_NO_EXPORT PyObject *
io_pack(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
{
PyObject *obj; // 输入的 Python 对象
int axis = NPY_RAVEL_AXIS; // 指定的轴,默认为展平
static char *kwlist[] = {"in", "axis", "bitorder", NULL}; // 关键字参数列表
char c = 'b'; // 默认的字节顺序为 'b' (big-endian)
const char * order_str = NULL; // 指定的字节顺序字符串
// 解析 Python 的参数元组和关键字参数
if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&s:pack" , kwlist,
&obj, PyArray_AxisConverter, &axis, &order_str)) {
// 解析失败时返回空指针
return NULL;
}
// 如果指定了字节顺序字符串,则根据字符串设置字节顺序
if (order_str != NULL) {
if (strncmp(order_str, "little", 6) == 0)
c = 'l'; // 如果字符串为 "little",设置为小端序
else if (strncmp(order_str, "big", 3) == 0)
c = 'b'; // 如果字符串为 "big",设置为大端序
else {
// 如果字符串既不是 "little" 也不是 "big",抛出数值错误异常
PyErr_SetString(PyExc_ValueError,
"'order' must be either 'little' or 'big'");
return NULL;
}
}
// 调用 pack_bits 函数进行打包操作,并返回其结果
return pack_bits(obj, axis, c);
}
NPY_NO_EXPORT PyObject *
io_unpack(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
{
PyObject *obj; // 输入的 Python 对象
int axis = NPY_RAVEL_AXIS; // 指定的轴,默认为展平
PyObject *count = Py_None; // 解包的位数
static char *kwlist[] = {"in", "axis", "count", "bitorder", NULL}; // 关键字参数列表
const char * c = NULL; // 指定的字节顺序字符串
// 解析 Python 的参数元组和关键字参数
if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&Os:unpack" , kwlist,
&obj, PyArray_AxisConverter, &axis, &count, &c)) {
// 解析失败时返回空指针
return NULL;
}
// 如果没有指定字节顺序,则默认为 'b' (big-endian)
if (c == NULL) {
c = "b";
}
// 检查指定的字节顺序是否有效,必须以 'l' 或 'b' 开头
if (c[0] != 'l' && c[0] != 'b') {
// 如果不是有效的顺序,抛出数值错误异常
PyErr_SetString(PyExc_ValueError,
"'order' must begin with 'l' or 'b'");
return NULL;
}
// 调用 unpack_bits 函数进行解包操作,并返回其结果
return unpack_bits(obj, axis, count, c[0]);
}
.\numpy\numpy\_core\src\multiarray\compiled_base.h
// 声明不导出的函数arr_place,用于执行某种数组操作
NPY_NO_EXPORT PyObject *
arr_place(PyObject *, PyObject *, PyObject *);
// 声明不导出的函数arr_bincount,用于计算数组中每个值的出现次数
NPY_NO_EXPORT PyObject *
arr_bincount(PyObject *, PyObject *const *, Py_ssize_t, PyObject *);
// 声明不导出的函数arr__monotonicity,用于处理数组的单调性
NPY_NO_EXPORT PyObject *
arr__monotonicity(PyObject *, PyObject *, PyObject *kwds);
// 声明不导出的函数arr_interp,用于在数组上进行插值
NPY_NO_EXPORT PyObject *
arr_interp(PyObject *, PyObject *const *, Py_ssize_t, PyObject *, PyObject *);
// 声明不导出的函数arr_interp_complex,用于在复数数组上进行插值
NPY_NO_EXPORT PyObject *
arr_interp_complex(PyObject *, PyObject *const *, Py_ssize_t, PyObject *, PyObject *);
// 声明不导出的函数arr_ravel_multi_index,用于将多维数组索引展平为一维索引
NPY_NO_EXPORT PyObject *
arr_ravel_multi_index(PyObject *, PyObject *, PyObject *);
// 声明不导出的函数arr_unravel_index,用于将一维索引展开为多维数组索引
NPY_NO_EXPORT PyObject *
arr_unravel_index(PyObject *, PyObject *, PyObject *);
// 声明不导出的函数arr_add_docstring,用于给数组对象添加文档字符串
NPY_NO_EXPORT PyObject *
arr_add_docstring(PyObject *, PyObject *const *, Py_ssize_t);
// 声明不导出的函数io_pack,用于打包数据到某种格式
NPY_NO_EXPORT PyObject *
io_pack(PyObject *, PyObject *, PyObject *);
// 声明不导出的函数io_unpack,用于从某种格式解包数据
NPY_NO_EXPORT PyObject *
io_unpack(PyObject *, PyObject *, PyObject *);
.\numpy\numpy\_core\src\multiarray\conversion_utils.c
/*
* 定义 NPY_NO_DEPRECATED_API 并设置为 NPY_API_VERSION
* 定义 _MULTIARRAYMODULE
*/
/*
* 引入必要的头文件和模块
*/
/*
* 定义静态函数 PyArray_PyIntAsInt_ErrMsg
* 用于将 PyObject 转换为 int,出错时打印错误消息
*/
static int
PyArray_PyIntAsInt_ErrMsg(PyObject *o, const char * msg) NPY_GCC_NONNULL(2);
/*
* 定义静态函数 PyArray_PyIntAsIntp_ErrMsg
* 用于将 PyObject 转换为 npy_intp,出错时打印错误消息
*/
static npy_intp
PyArray_PyIntAsIntp_ErrMsg(PyObject *o, const char * msg) NPY_GCC_NONNULL(2);
/****************************************************************
* PyArg_ParseTuple 使用的转换函数
****************************************************************/
/*NUMPY_API
*
* PyArg_ParseTuple 中使用的转换函数,用于处理 O& 参数
*
* 如果传入的对象是数组类型,则直接返回该对象,并增加引用计数
* 否则,尝试将对象转换为 NPY_ARRAY_CARRAY 类型的数组对象
* 需要在使用 PyArray_Converter 后对返回的数组对象进行 DECREF
*/
NPY_NO_EXPORT int
PyArray_Converter(PyObject *object, PyObject **address)
{
if (PyArray_Check(object)) {
*address = object;
Py_INCREF(object);
return NPY_SUCCEED;
}
else {
*address = PyArray_FROM_OF(object, NPY_ARRAY_CARRAY);
if (*address == NULL) {
return NPY_FAIL;
}
return NPY_SUCCEED;
}
}
/*NUMPY_API
* PyArg_ParseTuple 中使用的输出数组转换函数
*/
NPY_NO_EXPORT int
PyArray_OutputConverter(PyObject *object, PyArrayObject **address)
{
if (object == NULL || object == Py_None) {
*address = NULL;
return NPY_SUCCEED;
}
if (PyArray_Check(object)) {
*address = (PyArrayObject *)object;
return NPY_SUCCEED;
}
else {
PyErr_SetString(PyExc_TypeError,
"output must be an array");
*address = NULL;
return NPY_FAIL;
}
}
/*
* 将给定的值转换为整数,替代 PyArray_PyIntAsIntp,保留了旧版本的行为
*/
static inline npy_intp
dimension_from_scalar(PyObject *ob)
{
npy_intp value = PyArray_PyIntAsIntp(ob);
if (error_converting(value)) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
PyErr_SetString(PyExc_ValueError,
"Maximum allowed dimension exceeded");
}
return -1;
}
return value;
}
/*NUMPY_API
* 从序列中获取 intp 类型的块
*
* 此函数接受一个 Python 序列对象,并分配和填充一个 intp 数组以转换后的值。
*
* 在使用完毕后记得释放指针 seq.ptr,使用 PyDimMem_FREE(seq.ptr)**
*/
NPY_NO_EXPORT int
PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq)
{
seq->ptr = NULL; // 初始化指针为 NULL
seq->len = 0; // 初始化长度为 0
/*
* 当下面的弃用过期后,删除 `if` 语句,并更新 PyArray_OptionalIntpConverter 的注释。
*/
if (obj == Py_None) {
/* Numpy 1.20, 2020-05-31 */
// 如果 obj 是 None,则发出弃用警告,并返回成功
if (DEPRECATE(
"Passing None into shape arguments as an alias for () is "
"deprecated.") < 0){
return NPY_FAIL;
}
return NPY_SUCCEED;
}
PyObject *seq_obj = NULL;
/*
* 如果 obj 是标量,则尽快跳转到 dimension_from_scalar,跳过所有无用的计算。
*/
if (!PyLong_CheckExact(obj) && PySequence_Check(obj)) {
// 如果 obj 不是精确的长整型且是序列,则快速获取序列对象
seq_obj = PySequence_Fast(obj,
"expected a sequence of integers or a single integer.");
if (seq_obj == NULL) {
/* 继续尝试解析为单个整数。 */
PyErr_Clear();
}
}
if (seq_obj == NULL) {
/*
* obj 可能是标量(如果 dimension_from_scalar 没有失败的话,在此刻还未执行检查以验证此假设)。
*/
seq->ptr = npy_alloc_cache_dim(1); // 分配大小为 1 的缓存维度
if (seq->ptr == NULL) {
PyErr_NoMemory(); // 内存分配失败的错误处理
return NPY_FAIL;
}
else {
seq->len = 1; // 设置长度为 1
seq->ptr[0] = dimension_from_scalar(obj); // 将标量转换为维度值
if (error_converting(seq->ptr[0])) {
/*
* 如果发生的错误是类型错误(无法将值转换为整数),则告知用户预期的序列或整数。
*/
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError,
"expected a sequence of integers or a single "
"integer, got '%.100R'", obj);
}
npy_free_cache_dim_obj(*seq); // 释放分配的缓存维度对象
seq->ptr = NULL; // 将指针置为 NULL
return NPY_FAIL;
}
}
}
else {
/*
* `obj` is a sequence converted to the `PySequence_Fast` in `seq_obj`
* `len` stores the size of the sequence `seq_obj`
*/
Py_ssize_t len = PySequence_Fast_GET_SIZE(seq_obj);
// Check if the length of the sequence exceeds the maximum supported dimensions
if (len > NPY_MAXDIMS) {
PyErr_Format(PyExc_ValueError,
"maximum supported dimension for an ndarray "
"is currently %d, found %d", NPY_MAXDIMS, len);
Py_DECREF(seq_obj);
return NPY_FAIL;
}
// Allocate memory for `seq->ptr` if the sequence length is greater than 0
if (len > 0) {
seq->ptr = npy_alloc_cache_dim(len);
if (seq->ptr == NULL) {
PyErr_NoMemory();
Py_DECREF(seq_obj);
return NPY_FAIL;
}
}
// Set `seq->len` to the length of the sequence
seq->len = len;
// Convert Python index sequence `seq_obj` to C array `seq->ptr` of type `npy_intp`
int nd = PyArray_IntpFromIndexSequence(seq_obj,
(npy_intp *)seq->ptr, len);
Py_DECREF(seq_obj);
// Check if conversion was successful and dimensions match
if (nd == -1 || nd != len) {
// Free allocated memory and set `seq->ptr` to NULL on failure
npy_free_cache_dim_obj(*seq);
seq->ptr = NULL;
return NPY_FAIL;
}
}
// Return success status
return NPY_SUCCEED;
/*
* Like PyArray_IntpConverter, but leaves `seq` untouched if `None` is passed
* rather than treating `None` as `()`.
*/
NPY_NO_EXPORT int
PyArray_OptionalIntpConverter(PyObject *obj, PyArray_Dims *seq)
{
// 检查是否传入了 None 对象
if (obj == Py_None) {
// 如果是 None,直接返回成功,不做任何操作
return NPY_SUCCEED;
}
// 否则调用 PyArray_IntpConverter 处理传入的对象
return PyArray_IntpConverter(obj, seq);
}
NPY_NO_EXPORT int
PyArray_CopyConverter(PyObject *obj, NPY_COPYMODE *copymode) {
// 检查是否传入了 None 对象
if (obj == Py_None) {
// 如果是 None,设置复制模式为 NPY_COPY_IF_NEEDED,并返回成功
*copymode = NPY_COPY_IF_NEEDED;
return NPY_SUCCEED;
}
int int_copymode;
// 检查传入对象的类型是否为 _CopyMode
if ((PyObject *)Py_TYPE(obj) == npy_static_pydata._CopyMode) {
// 获取 _CopyMode 对象的 "value" 属性值
PyObject* mode_value = PyObject_GetAttrString(obj, "value");
if (mode_value == NULL) {
return NPY_FAIL;
}
// 将属性值转换为整数
int_copymode = (int)PyLong_AsLong(mode_value);
Py_DECREF(mode_value);
// 检查转换是否出错
if (error_converting(int_copymode)) {
return NPY_FAIL;
}
}
else if(PyUnicode_Check(obj)) {
// 如果传入对象是字符串,则返回错误,不允许用于 'copy' 关键字
PyErr_SetString(PyExc_ValueError,
"strings are not allowed for 'copy' keyword. "
"Use True/False/None instead.");
return NPY_FAIL;
}
else {
// 对于其他对象,尝试使用 PyArray_BoolConverter 转换为布尔值
npy_bool bool_copymode;
if (!PyArray_BoolConverter(obj, &bool_copymode)) {
return NPY_FAIL;
}
int_copymode = (int)bool_copymode;
}
// 设置复制模式,并返回成功
*copymode = (NPY_COPYMODE)int_copymode;
return NPY_SUCCEED;
}
NPY_NO_EXPORT int
PyArray_AsTypeCopyConverter(PyObject *obj, NPY_ASTYPECOPYMODE *copymode)
{
int int_copymode;
// 检查传入对象的类型是否为 _CopyMode
if ((PyObject *)Py_TYPE(obj) == npy_static_pydata._CopyMode) {
// 不允许使用 _CopyMode 枚举类型,返回错误
PyErr_SetString(PyExc_ValueError,
"_CopyMode enum is not allowed for astype function. "
"Use true/false instead.");
return NPY_FAIL;
}
else {
// 否则,尝试使用 PyArray_BoolConverter 转换为布尔值
npy_bool bool_copymode;
if (!PyArray_BoolConverter(obj, &bool_copymode)) {
return NPY_FAIL;
}
int_copymode = (int)bool_copymode;
}
// 设置复制模式,并返回成功
*copymode = (NPY_ASTYPECOPYMODE)int_copymode;
return NPY_SUCCEED;
}
/*NUMPY_API
* Get buffer chunk from object
*
* this function takes a Python object which exposes the (single-segment)
* buffer interface and returns a pointer to the data segment
*
* You should increment the reference count by one of buf->base
* if you will hang on to a reference
*
* You only get a borrowed reference to the object. Do not free the
* memory...
*/
NPY_NO_EXPORT int
PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf)
{
Py_buffer view;
buf->ptr = NULL;
buf->flags = NPY_ARRAY_BEHAVED;
buf->base = NULL;
// 检查是否传入了 None 对象
if (obj == Py_None) {
// 如果是 None,直接返回成功,不做任何操作
return NPY_SUCCEED;
}
// 如果传入对象不是 None,则继续处理
// (此处为示例中代码截断,未能提供完整的函数内容)
}
/*
* 如果无法获取对象的缓冲区视图,清除错误并标记缓冲区为不可写。
* 然后再次尝试获取缓冲区视图,这次只要求连续且简单的缓冲区。
*/
if (PyObject_GetBuffer(obj, &view,
PyBUF_ANY_CONTIGUOUS|PyBUF_WRITABLE|PyBUF_SIMPLE) != 0) {
PyErr_Clear();
buf->flags &= ~NPY_ARRAY_WRITEABLE;
if (PyObject_GetBuffer(obj, &view,
PyBUF_ANY_CONTIGUOUS|PyBUF_SIMPLE) != 0) {
return NPY_FAIL;
}
}
/*
* 将缓冲区视图的指针和长度分配给 buf 结构体。
*/
buf->ptr = view.buf;
buf->len = (npy_intp) view.len;
/*
* 在 Python 3 中,此段代码替换了被弃用的 PyObject_AsWriteBuffer 和
* PyObject_AsReadBuffer 函数。这些函数释放了缓冲区。由提供缓冲区的对象
* 负责在释放后保证缓冲区的有效性。
*/
PyBuffer_Release(&view);
/*
* 如果对象是内存视图类型,则将 buf 的 base 指向其基础对象。
*/
if (PyMemoryView_Check(obj)) {
buf->base = PyMemoryView_GET_BASE(obj);
}
/*
* 如果 buf 的 base 仍然为空,则将其设置为当前对象。
*/
if (buf->base == NULL) {
buf->base = obj;
}
/*
* 返回操作成功的标志。
*/
return NPY_SUCCEED;
/*NUMPY_API
* Get axis from an object (possibly None) -- a converter function,
*
* See also PyArray_ConvertMultiAxis, which also handles a tuple of axes.
*/
NPY_NO_EXPORT int
PyArray_AxisConverter(PyObject *obj, int *axis)
{
// 如果对象是 None,则设置 axis 为 NPY_RAVEL_AXIS
if (obj == Py_None) {
*axis = NPY_RAVEL_AXIS;
}
// 否则,尝试将对象转换为整数作为 axis
else {
*axis = PyArray_PyIntAsInt_ErrMsg(obj,
"an integer is required for the axis");
// 如果转换过程中出现错误,返回失败
if (error_converting(*axis)) {
return NPY_FAIL;
}
}
// 返回成功
return NPY_SUCCEED;
}
/*
* Converts an axis parameter into an ndim-length C-array of
* boolean flags, True for each axis specified.
*
* If obj is None or NULL, everything is set to True. If obj is a tuple,
* each axis within the tuple is set to True. If obj is an integer,
* just that axis is set to True.
*/
NPY_NO_EXPORT int
PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags)
{
/* None means all of the axes */
// 如果 axis_in 是 None 或 NULL,则将所有轴设置为 True
if (axis_in == Py_None || axis_in == NULL) {
memset(out_axis_flags, 1, ndim);
return NPY_SUCCEED;
}
/* A tuple of which axes */
// 如果 axis_in 是一个元组,则根据元组中的每个轴设置对应位置为 True
else if (PyTuple_Check(axis_in)) {
int i, naxes;
memset(out_axis_flags, 0, ndim);
naxes = PyTuple_Size(axis_in);
if (naxes < 0) {
return NPY_FAIL;
}
// 遍历元组中的每个元素,将对应的轴设置为 True
for (i = 0; i < naxes; ++i) {
PyObject *tmp = PyTuple_GET_ITEM(axis_in, i);
// 尝试将元组元素转换为整数轴
int axis = PyArray_PyIntAsInt_ErrMsg(tmp,
"integers are required for the axis tuple elements");
// 如果转换过程中出现错误,返回失败
if (error_converting(axis)) {
return NPY_FAIL;
}
// 检查和调整轴的有效性
if (check_and_adjust_axis(&axis, ndim) < 0) {
return NPY_FAIL;
}
// 检查是否有重复的轴值
if (out_axis_flags[axis]) {
PyErr_SetString(PyExc_ValueError,
"duplicate value in 'axis'");
return NPY_FAIL;
}
// 将对应轴位置设置为 True
out_axis_flags[axis] = 1;
}
return NPY_SUCCEED;
}
/* Try to interpret axis as an integer */
// 否则,尝试将 axis_in 解释为一个整数轴
else {
int axis;
memset(out_axis_flags, 0, ndim);
// 尝试将 axis_in 转换为整数轴
axis = PyArray_PyIntAsInt_ErrMsg(axis_in,
"an integer is required for the axis");
// 如果转换过程中出现错误,返回失败
if (error_converting(axis)) {
return NPY_FAIL;
}
/*
* Special case letting axis={-1,0} slip through for scalars,
* for backwards compatibility reasons.
*/
// 对于零维数组(标量),允许 axis 取值为 {-1, 0},这是为了向后兼容
if (ndim == 0 && (axis == 0 || axis == -1)) {
return NPY_SUCCEED;
}
// 检查和调整轴的有效性
if (check_and_adjust_axis(&axis, ndim) < 0) {
return NPY_FAIL;
}
// 将对应轴位置设置为 True
out_axis_flags[axis] = 1;
return NPY_SUCCEED;
}
}
/*NUMPY_API
* Convert an object to true / false
*/
NPY_NO_EXPORT int
PyArray_BoolConverter(PyObject *object, npy_bool *val)
{
// 将对象转换为布尔值
if (PyObject_IsTrue(object)) {
*val = NPY_TRUE;
}
else {
*val = NPY_FALSE;
}
*val = NPY_FALSE;
}
// 返回成功
return NPY_SUCCEED;
}
// 检查是否有 Python 异常发生
if (PyErr_Occurred()) {
// 如果有异常发生,则返回失败标志
return NPY_FAIL;
}
// 如果没有异常发生,则返回成功标志
return NPY_SUCCEED;
}
/*
* Optionally convert an object to true / false
*/
NPY_NO_EXPORT int
PyArray_OptionalBoolConverter(PyObject *object, int *val)
{
/* Leave the desired default from the caller for Py_None */
// 如果对象是 Py_None,则使用调用者指定的默认值
if (object == Py_None) {
return NPY_SUCCEED;
}
// 如果对象能被解释为 True,则将 *val 设置为 1
if (PyObject_IsTrue(object)) {
*val = 1;
}
else {
// 否则将 *val 设置为 0
*val = 0;
}
// 如果出现错误,返回 NPY_FAIL
if (PyErr_Occurred()) {
return NPY_FAIL;
}
// 操作成功,返回 NPY_SUCCEED
return NPY_SUCCEED;
}
static int
string_converter_helper(
PyObject *object,
void *out,
int (*str_func)(char const*, Py_ssize_t, void*),
char const *name,
char const *message)
{
/* allow bytes for compatibility */
// 允许 bytes 类型以保持兼容性
PyObject *str_object = NULL;
// 如果对象是 bytes 类型,则转换为 Unicode
if (PyBytes_Check(object)) {
str_object = PyUnicode_FromEncodedObject(object, NULL, NULL);
// 转换失败,抛出 ValueError 异常
if (str_object == NULL) {
PyErr_Format(PyExc_ValueError,
"%s %s (got %R)", name, message, object);
return NPY_FAIL;
}
}
// 如果对象是 Unicode 类型,直接使用
else if (PyUnicode_Check(object)) {
str_object = object;
Py_INCREF(str_object);
}
else {
// 其它类型抛出 TypeError 异常
PyErr_Format(PyExc_TypeError,
"%s must be str, not %s", name, Py_TYPE(object)->tp_name);
return NPY_FAIL;
}
Py_ssize_t length;
// 将 Unicode 对象转换为 UTF-8 编码的 C 字符串
char const *str = PyUnicode_AsUTF8AndSize(str_object, &length);
if (str == NULL) {
Py_DECREF(str_object);
return NPY_FAIL;
}
// 调用指定的 str_func 处理字符串
int ret = str_func(str, length, out);
Py_DECREF(str_object);
// 如果 str_func 返回负数且未设置异常,则抛出 ValueError 异常
if (ret < 0) {
if (!PyErr_Occurred()) {
PyErr_Format(PyExc_ValueError,
"%s %s (got %R)", name, message, object);
}
return NPY_FAIL;
}
// 操作成功,返回 NPY_SUCCEED
return NPY_SUCCEED;
}
static int byteorder_parser(char const *str, Py_ssize_t length, void *data)
{
char *endian = (char *)data;
// 如果字符串长度小于 1,返回 -1
if (length < 1) {
return -1;
}
// 解析字节顺序字符
else if (str[0] == NPY_BIG || str[0] == NPY_LITTLE ||
str[0] == NPY_NATIVE || str[0] == NPY_IGNORE) {
*endian = str[0];
return 0;
}
else if (str[0] == 'b' || str[0] == 'B') {
*endian = NPY_BIG;
return 0;
}
else if (str[0] == 'l' || str[0] == 'L') {
*endian = NPY_LITTLE;
return 0;
}
else if (str[0] == 'n' || str[0] == 'N') {
*endian = NPY_NATIVE;
return 0;
}
else if (str[0] == 'i' || str[0] == 'I') {
*endian = NPY_IGNORE;
return 0;
}
else if (str[0] == 's' || str[0] == 'S') {
*endian = NPY_SWAP;
return 0;
}
else {
// 未识别的字符,返回 -1
return -1;
}
}
/*NUMPY_API
* Convert object to endian
*/
NPY_NO_EXPORT int
PyArray_ByteorderConverter(PyObject *obj, char *endian)
{
// 调用通用字符串转换函数处理字节顺序
return string_converter_helper(
obj, (void *)endian, byteorder_parser, "byteorder", "not recognized");
}
static int sortkind_parser(char const *str, Py_ssize_t length, void *data)
{
NPY_SORTKIND *sortkind = (NPY_SORTKIND *)data;
if (length < 1) {
return -1;
}
if (str[0] == 'q' || str[0] == 'Q') {
*sortkind = NPY_QUICKSORT;
return 0;
}
else if (str[0] == 'h' || str[0] == 'H') {
*sortkind = NPY_HEAPSORT;
return 0;
}
else if (str[0] == 'm' || str[0] == 'M') {
/*
* Mergesort 是 NPY_STABLESORT 的别名。
* 这样做保持了向后兼容性,同时允许使用其他类型的稳定排序。
*/
*sortkind = NPY_MERGESORT;
return 0;
}
else if (str[0] == 's' || str[0] == 'S') {
/*
* NPY_STABLESORT 是以下之一:
*
* - mergesort
* - timsort
*
* 具体使用哪种取决于数据类型。
*/
*sortkind = NPY_STABLESORT;
return 0;
}
else {
return -1;
}
/*NUMPY_API
* Convert object to sort kind
*/
NPY_NO_EXPORT int
PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind)
{
/* Leave the desired default from the caller for Py_None */
if (obj == Py_None) {
return NPY_SUCCEED;
}
/* 使用帮助函数进行字符串转换,将结果保存到 sortkind 指针所指向的位置 */
return string_converter_helper(
obj, (void *)sortkind, sortkind_parser, "sort kind",
"must be one of 'quick', 'heap', or 'stable'");
}
/* 定义 selectkind_parser 函数,用于解析选择类型的字符串 */
static int selectkind_parser(char const *str, Py_ssize_t length, void *data)
{
NPY_SELECTKIND *selectkind = (NPY_SELECTKIND *)data;
if (length == 11 && strcmp(str, "introselect") == 0) {
*selectkind = NPY_INTROSELECT;
return 0;
}
else {
return -1;
}
}
/*NUMPY_API
* Convert object to select kind
*/
NPY_NO_EXPORT int
PyArray_SelectkindConverter(PyObject *obj, NPY_SELECTKIND *selectkind)
{
/* 使用帮助函数进行字符串转换,将结果保存到 selectkind 指针所指向的位置 */
return string_converter_helper(
obj, (void *)selectkind, selectkind_parser, "select kind",
"must be 'introselect'");
}
/* 定义 searchside_parser 函数,用于解析搜索方向的字符串 */
static int searchside_parser(char const *str, Py_ssize_t length, void *data)
{
NPY_SEARCHSIDE *side = (NPY_SEARCHSIDE *)data;
int is_exact = 0;
if (length < 1) {
return -1;
}
else if (str[0] == 'l' || str[0] == 'L') {
*side = NPY_SEARCHLEFT;
is_exact = (length == 4 && strcmp(str, "left") == 0);
}
else if (str[0] == 'r' || str[0] == 'R') {
*side = NPY_SEARCHRIGHT;
is_exact = (length == 5 && strcmp(str, "right") == 0);
}
else {
return -1;
}
/* 如果不是精确匹配,产生 DeprecationWarning */
if (!is_exact) {
/* NumPy 1.20, 2020-05-19 */
if (DEPRECATE("inexact matches and case insensitive matches "
"for search side are deprecated, please use "
"one of 'left' or 'right' instead.") < 0) {
return -1;
}
}
return 0;
}
/*NUMPY_API
* Convert object to searchsorted side
*/
NPY_NO_EXPORT int
PyArray_SearchsideConverter(PyObject *obj, void *addr)
{
/* 使用帮助函数进行字符串转换,将结果保存到 addr 所指向的位置 */
return string_converter_helper(
obj, addr, searchside_parser, "search side",
"must be 'left' or 'right'");
}
/* 定义 order_parser 函数,用于解析数组排序顺序的字符串 */
static int order_parser(char const *str, Py_ssize_t length, void *data)
{
NPY_ORDER *val = (NPY_ORDER *)data;
if (length != 1) {
return -1;
}
if (str[0] == 'C' || str[0] == 'c') {
*val = NPY_CORDER;
return 0;
}
else if (str[0] == 'F' || str[0] == 'f') {
*val = NPY_FORTRANORDER;
return 0;
}
else if (str[0] == 'A' || str[0] == 'a') {
*val = NPY_ANYORDER;
return 0;
}
else if (str[0] == 'K' || str[0] == 'k') {
*val = NPY_KEEPORDER;
return 0;
}
else {
return -1;
}
}
/*NUMPY_API
* Convert an object to FORTRAN / C / ANY / KEEP
*/
NPY_NO_EXPORT int
PyArray_OrderConverter(PyObject *object, NPY_ORDER *val)
{
/* 如果 object 是 Py_None,则使用调用者期望的默认值 */
if (object == Py_None) {
// 如果 object 是 Py_None,则直接返回成功
return NPY_SUCCEED;
}
// 否则,调用字符串转换助手函数,将 object 转换为字符串,并存储到 val 中
return string_converter_helper(
object, (void *)val, order_parser, "order",
"must be one of 'C', 'F', 'A', or 'K'");
}
/* 解析剪裁模式字符串并转换为枚举值 */
static int clipmode_parser(char const *str, Py_ssize_t length, void *data)
{
NPY_CLIPMODE *val = (NPY_CLIPMODE *)data;
int is_exact = 0;
// 检查字符串长度,如果小于1则返回错误
if (length < 1) {
return -1;
}
// 根据字符串首字符确定剪裁模式
if (str[0] == 'C' || str[0] == 'c') {
*val = NPY_CLIP;
is_exact = (length == 4 && strcmp(str, "clip") == 0); // 检查是否精确匹配
}
else if (str[0] == 'W' || str[0] == 'w') {
*val = NPY_WRAP;
is_exact = (length == 4 && strcmp(str, "wrap") == 0); // 检查是否精确匹配
}
else if (str[0] == 'R' || str[0] == 'r') {
*val = NPY_RAISE;
is_exact = (length == 5 && strcmp(str, "raise") == 0); // 检查是否精确匹配
}
else {
return -1; // 如果首字符不符合预期,则返回错误
}
/* 过滤掉大小写不敏感或非精确匹配的输入,并输出 DeprecationWarning */
if (!is_exact) {
/* Numpy 1.20, 2020-05-19 */
if (DEPRECATE("inexact matches and case insensitive matches "
"for clip mode are deprecated, please use "
"one of 'clip', 'raise', or 'wrap' instead.") < 0) {
return -1;
}
}
return 0; // 解析成功返回 0
}
/*NUMPY_API
* 将对象转换为 NPY_RAISE / NPY_CLIP / NPY_WRAP
*/
NPY_NO_EXPORT int
PyArray_ClipmodeConverter(PyObject *object, NPY_CLIPMODE *val)
{
// 如果对象为空或为 None,则设置为 NPY_RAISE
if (object == NULL || object == Py_None) {
*val = NPY_RAISE;
}
// 如果对象是字节串或 Unicode 字符串,则调用字符串转换器助手函数进行处理
else if (PyBytes_Check(object) || PyUnicode_Check(object)) {
return string_converter_helper(
object, (void *)val, clipmode_parser, "clipmode",
"must be one of 'clip', 'raise', or 'wrap'");
}
else {
/* 对于传递了 `RAISE`, `WRAP`, `CLIP` 的用户 */
int number = PyArray_PyIntAsInt(object);
if (error_converting(number)) {
goto fail;
}
if (number <= (int) NPY_RAISE
&& number >= (int) NPY_CLIP) {
*val = (NPY_CLIPMODE) number;
}
else {
PyErr_Format(PyExc_ValueError,
"integer clipmode must be RAISE, WRAP, or CLIP "
"from 'numpy._core.multiarray'");
}
}
return NPY_SUCCEED;
fail:
PyErr_SetString(PyExc_TypeError,
"clipmode not understood");
return NPY_FAIL;
}
/*NUMPY_API
* 将对象转换为包含 n 个 NPY_CLIPMODE 值的数组。
* 这用于需要为每个轴应用不同模式的函数,如 ravel_multi_index。
*/
NPY_NO_EXPORT int
PyArray_ConvertClipmodeSequence(PyObject *object, NPY_CLIPMODE *modes, int n)
{
int i;
/* 获取剪裁模式(s) */
// 检查对象是否存在且为元组或列表
if (object && (PyTuple_Check(object) || PyList_Check(object))) {
// 如果对象是序列且长度不等于 n,则抛出长度错误异常并返回失败
if (PySequence_Size(object) != n) {
PyErr_Format(PyExc_ValueError,
"list of clipmodes has wrong length (%zd instead of %d)",
PySequence_Size(object), n);
return NPY_FAIL;
}
// 遍历对象的每个元素
for (i = 0; i < n; ++i) {
// 获取序列中的第 i 个元素
PyObject *item = PySequence_GetItem(object, i);
// 如果获取元素失败,则返回失败
if(item == NULL) {
return NPY_FAIL;
}
// 尝试将元素转换为数组剪裁模式,若转换失败则释放元素并返回失败
if(PyArray_ClipmodeConverter(item, &modes[i]) != NPY_SUCCEED) {
Py_DECREF(item);
return NPY_FAIL;
}
// 释放元素引用
Py_DECREF(item);
}
}
// 如果对象不是序列,尝试将其作为单个剪裁模式处理
else if (PyArray_ClipmodeConverter(object, &modes[0]) == NPY_SUCCEED) {
// 如果成功转换为剪裁模式,则将该模式应用到所有 modes 数组元素中
for (i = 1; i < n; ++i) {
modes[i] = modes[0];
}
}
// 如果对象既不是序列也无法转换为剪裁模式,则返回失败
else {
return NPY_FAIL;
}
// 若所有操作成功完成,则返回成功
return NPY_SUCCEED;
/*
* Parse and interpret a string representation of correlation mode
* to an NPY_CORRELATEMODE value.
*/
static int correlatemode_parser(char const *str, Py_ssize_t length, void *data)
{
NPY_CORRELATEMODE *val = (NPY_CORRELATEMODE *)data;
int is_exact = 0;
// Ensure the input string is not empty
if (length < 1) {
return -1;
}
// Check for case-insensitive matching for 'valid'
if (str[0] == 'V' || str[0] == 'v') {
*val = NPY_VALID;
is_exact = (length == 5 && strcmp(str, "valid") == 0);
}
// Check for case-insensitive matching for 'same'
else if (str[0] == 'S' || str[0] == 's') {
*val = NPY_SAME;
is_exact = (length == 4 && strcmp(str, "same") == 0);
}
// Check for case-insensitive matching for 'full'
else if (str[0] == 'F' || str[0] == 'f') {
*val = NPY_FULL;
is_exact = (length == 4 && strcmp(str, "full") == 0);
}
else {
// Invalid mode string
return -1;
}
/*
* If the match was not exact (case-sensitive),
* issue a deprecation warning.
*/
if (!is_exact) {
/* Numpy 1.21, 2021-01-19 */
// Issue a deprecation warning for inexact matches
if (DEPRECATE("inexact matches and case insensitive matches for "
"convolve/correlate mode are deprecated, please "
"use one of 'valid', 'same', or 'full' instead.") < 0) {
return -1;
}
}
// Parsing successful
return 0;
}
/*
* Convert an object to NPY_VALID / NPY_SAME / NPY_FULL
*/
NPY_NO_EXPORT int
PyArray_CorrelatemodeConverter(PyObject *object, NPY_CORRELATEMODE *val)
{
// Check if the object is a Unicode string
if (PyUnicode_Check(object)) {
// Use string_converter_helper to convert the Unicode object
return string_converter_helper(
object, (void *)val, correlatemode_parser, "mode",
"must be one of 'valid', 'same', or 'full'");
}
// If the object is not a Unicode string
else {
// Attempt conversion assuming it's an integer
int number = PyArray_PyIntAsInt(object);
// Handle conversion errors
if (error_converting(number)) {
PyErr_SetString(PyExc_TypeError,
"convolve/correlate mode not understood");
return NPY_FAIL;
}
// Check if the integer is within the valid range for modes
if (number <= (int) NPY_FULL && number >= (int) NPY_VALID) {
*val = (NPY_CORRELATEMODE) number;
return NPY_SUCCEED;
}
else {
// Invalid integer mode value
PyErr_Format(PyExc_ValueError,
"integer convolve/correlate mode must be 0, 1, or 2");
return NPY_FAIL;
}
}
}
# 当输入的 case 是 's' 时执行以下代码块
case 's':
# 如果输入长度为 6 并且字符串与 "unsafe" 完全相同
if (length == 6 && strcmp(str, "unsafe") == 0) {
# 设置 *casting 指针指向的值为 NPY_UNSAFE_CASTING
*casting = NPY_UNSAFE_CASTING;
# 返回 0 表示成功匹配并设置
return 0;
}
# 如果条件不满足则跳出 switch 结构
break;
}
# 默认情况返回 -1,表示未找到匹配项
return -1;
/*NUMPY_API
* 将任何 Python 对象 *obj* 转换为一个 NPY_CASTING 枚举。
*/
NPY_NO_EXPORT int
PyArray_CastingConverter(PyObject *obj, NPY_CASTING *casting)
{
// 调用辅助函数进行字符串转换
return string_converter_helper(
obj, (void *)casting, casting_parser, "casting",
"must be one of 'no', 'equiv', 'safe', "
"'same_kind', or 'unsafe'");
// 返回 0 表示成功
return 0;
}
/*****************************
* 其他转换函数
*****************************/
static int
PyArray_PyIntAsInt_ErrMsg(PyObject *o, const char * msg)
{
npy_intp long_value;
/* 这里假设 NPY_SIZEOF_INTP >= NPY_SIZEOF_INT */
long_value = PyArray_PyIntAsIntp_ErrMsg(o, msg);
#if (NPY_SIZEOF_INTP > NPY_SIZEOF_INT)
// 如果 long_value 超出 int 的范围,则抛出 ValueError
if ((long_value < INT_MIN) || (long_value > INT_MAX)) {
PyErr_SetString(PyExc_ValueError, "integer won't fit into a C int");
return -1;
}
#endif
// 返回 long_value 强制转换为 int 类型
return (int) long_value;
}
/*NUMPY_API*/
NPY_NO_EXPORT int
PyArray_PyIntAsInt(PyObject *o)
{
// 调用带错误消息的 PyArray_PyIntAsInt_ErrMsg 函数
return PyArray_PyIntAsInt_ErrMsg(o, "an integer is required");
}
static npy_intp
PyArray_PyIntAsIntp_ErrMsg(PyObject *o, const char * msg)
{
#if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP)
long long long_value = -1;
#else
long long_value = -1;
#endif
PyObject *obj, *err;
/*
* 更加严格,不允许 bool 值。
* np.bool 也被禁止,因为布尔数组目前不支持索引。
*/
// 如果 o 为空或者是 bool 值,或者是布尔类型数组,则抛出 TypeError
if (!o || PyBool_Check(o) || PyArray_IsScalar(o, Bool)) {
PyErr_SetString(PyExc_TypeError, msg);
return -1;
}
/*
* 因为这是通常的情况,首先检查 o 是否是整数。这是一个精确的检查,因为否则会使用 __index__ 方法。
*/
// 如果 o 是 PyLong_CheckExact,则直接将其转换为 long_value
if (PyLong_CheckExact(o)) {
#if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP)
long_value = PyLong_AsLongLong(o);
#else
long_value = PyLong_AsLong(o);
#endif
return (npy_intp)long_value;
}
/*
* 最一般的情况。PyNumber_Index(o) 包含了所有情况,包括数组。原则上,可以在停用后使用 PyIndex_AsSSize_t 替换整个函数。
*/
// 调用 PyNumber_Index(o) 获取索引,然后将其转换为 long_value
obj = PyNumber_Index(o);
if (obj == NULL) {
return -1;
}
#if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP)
long_value = PyLong_AsLongLong(obj);
#else
long_value = PyLong_AsLong(obj);
#endif
Py_DECREF(obj);
// 如果转换失败,则设置相应错误消息
if (error_converting(long_value)) {
err = PyErr_Occurred();
// 只在这里替换 TypeError,因为这是正常的错误情况。
if (PyErr_GivenExceptionMatches(err, PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError, msg);
}
return -1;
}
// 转换成功后检查溢出情况
goto overflow_check; /* 防止未使用的警告 */
overflow_check:
#if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP)
#if (NPY_SIZEOF_LONGLONG > NPY_SIZEOF_INTP)
// 检查 long_value 是否超出 numpy.intp 类型的范围
if ((long_value < NPY_MIN_INTP) || (long_value > NPY_MAX_INTP)) {
// 如果超出范围,设置 OverflowError 异常并返回错误码 -1
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C numpy.intp");
return -1;
}
#endif
#else
#if (NPY_SIZEOF_LONG > NPY_SIZEOF_INTP)
// 如果长整型的大小大于 npy_intp 的大小,则检查 long_value 是否超出 npy_intp 的范围
if ((long_value < NPY_MIN_INTP) || (long_value > NPY_MAX_INTP)) {
// 如果 long_value 超出范围,设置 OverflowError 并返回 -1
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C numpy.intp");
return -1;
}
#endif
#endif
// 返回 long_value
return long_value;
}
/*NUMPY_API*/
// 将 Python 中的整数对象 o 转换为 npy_intp 类型
NPY_NO_EXPORT npy_intp
PyArray_PyIntAsIntp(PyObject *o)
{
// 调用带错误消息的 PyArray_PyIntAsIntp_ErrMsg 函数
return PyArray_PyIntAsIntp_ErrMsg(o, "an integer is required");
}
NPY_NO_EXPORT int
// 将 Python 对象 o 转换为 npy_intp 类型的 C 数组指针 *val
PyArray_IntpFromPyIntConverter(PyObject *o, npy_intp *val)
{
// 调用 PyArray_PyIntAsIntp 将 o 转换为 npy_intp 类型,并将结果赋给 *val
*val = PyArray_PyIntAsIntp(o);
// 如果转换出错,返回 NPY_FAIL
if (error_converting(*val)) {
return NPY_FAIL;
}
// 成功转换,返回 NPY_SUCCEED
return NPY_SUCCEED;
}
/**
* 从整数序列中读取值并存储到数组中。
*
* @param seq 使用 `PySequence_Fast` 创建的序列。
* @param vals 用于存储维度的数组(必须足够大以容纳 `maxvals` 个值)。
* @param max_vals 可以写入 `vals` 的最大维度数。
* @return 维度数或如果发生错误则返回 -1。
*
* .. note::
*
* 与 PyArray_IntpFromSequence 相反,它使用并返回 `npy_intp`
* 作为值的数量。
*/
NPY_NO_EXPORT npy_intp
PyArray_IntpFromIndexSequence(PyObject *seq, npy_intp *vals, npy_intp maxvals)
{
/*
* 首先,检查序列是否是标量整数或是否可以“转换”为标量。
*/
Py_ssize_t nd = PySequence_Fast_GET_SIZE(seq);
PyObject *op;
for (Py_ssize_t i = 0; i < PyArray_MIN(nd, maxvals); i++) {
op = PySequence_Fast_GET_ITEM(seq, i);
// 调用 dimension_from_scalar 获取标量 op 的维度,并存储到 vals[i] 中
vals[i] = dimension_from_scalar(op);
// 如果转换出错,返回 -1
if (error_converting(vals[i])) {
return -1;
}
}
// 返回维度数 nd
return nd;
}
/*NUMPY_API
* PyArray_IntpFromSequence
* 返回转换的整数数目或如果发生错误则返回 -1。
* vals 必须足够大以容纳 maxvals
*/
NPY_NO_EXPORT int
// 从 Python 对象 seq 转换为 npy_intp 数组 vals
PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals)
{
PyObject *seq_obj = NULL;
// 如果 seq 不是长整数并且是序列类型
if (!PyLong_CheckExact(seq) && PySequence_Check(seq)) {
// 使用 PySequence_Fast 尝试快速获取序列对象 seq_obj
seq_obj = PySequence_Fast(seq,
"expected a sequence of integers or a single integer");
// 如果获取失败,继续尝试作为单个整数解析
if (seq_obj == NULL) {
PyErr_Clear();
}
}
// 如果 seq_obj 为空
if (seq_obj == NULL) {
// 将 seq 视为标量,并将其维度存储到 vals[0] 中
vals[0] = dimension_from_scalar(seq);
// 如果转换出错,抛出适当的 TypeError 异常
if (error_converting(vals[0])) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError,
"expected a sequence of integers or a single "
"integer, got '%.100R'", seq);
}
return -1;
}
// 返回 1 表示成功转换一个值
return 1;
}
else {
int res;
// 调用 PyArray_IntpFromIndexSequence 从 seq_obj 中获取 npy_intp 类型的值到 vals,并最多获取 maxvals 个值
res = PyArray_IntpFromIndexSequence(seq_obj, vals, (npy_intp)maxvals);
// 释放 seq_obj 对象的引用
Py_DECREF(seq_obj);
// 返回转换结果
return res;
}
}
/**
* WARNING: This flag is a bad idea, but was the only way to both
* 1) Support unpickling legacy pickles with object types.
* 2) Deprecate (and later disable) usage of O4 and O8
*
* The key problem is that the pickled representation unpickles by
* directly calling the dtype constructor, which has no way of knowing
* that it is in an unpickle context instead of a normal context without
* evil global state like we create here.
*/
NPY_NO_EXPORT NPY_TLS int evil_global_disable_warn_O4O8_flag = 0;
/*
* Convert a gentype (that is actually a generic kind character) and
* it's itemsize to a NUmPy typenumber, i.e. `itemsize=4` and `gentype='f'`
* becomes `NPY_FLOAT32`.
*/
NPY_NO_EXPORT int
PyArray_TypestrConvert(int itemsize, int gentype)
{
int newtype = NPY_NOTYPE;
switch (gentype) {
case NPY_GENBOOLLTR:
if (itemsize == 1) {
newtype = NPY_BOOL;
}
break;
case NPY_SIGNEDLTR:
switch(itemsize) {
case 1:
newtype = NPY_INT8;
break;
case 2:
newtype = NPY_INT16;
break;
case 4:
newtype = NPY_INT32;
break;
case 8:
newtype = NPY_INT64;
break;
#ifdef NPY_INT128
case 16:
newtype = NPY_INT128;
break;
#endif
}
break;
case NPY_UNSIGNEDLTR:
switch(itemsize) {
case 1:
newtype = NPY_UINT8;
break;
case 2:
newtype = NPY_UINT16;
break;
case 4:
newtype = NPY_UINT32;
break;
case 8:
newtype = NPY_UINT64;
break;
#ifdef NPY_INT128
case 16:
newtype = NPY_UINT128;
break;
#endif
}
break;
case NPY_FLOATINGLTR:
switch(itemsize) {
case 2:
newtype = NPY_FLOAT16;
break;
case 4:
newtype = NPY_FLOAT32;
break;
case 8:
newtype = NPY_FLOAT64;
break;
#ifdef NPY_FLOAT80
case 10:
newtype = NPY_FLOAT80;
break;
#endif
#ifdef NPY_FLOAT96
case 12:
newtype = NPY_FLOAT96;
break;
#endif
#ifdef NPY_FLOAT128
case 16:
newtype = NPY_FLOAT128;
break;
#endif
}
break;
}
// 返回对应的 NumPy 类型编号
return newtype;
}
#endif
}
break;
case NPY_COMPLEXLTR:
switch(itemsize) {
case 8:
// 设置新类型为复数类型 NPY_COMPLEX64
newtype = NPY_COMPLEX64;
break;
case 16:
// 设置新类型为复数类型 NPY_COMPLEX128
newtype = NPY_COMPLEX128;
break;
#ifdef NPY_FLOAT80
case 20:
// 设置新类型为复数类型 NPY_COMPLEX160(仅在 NPY_FLOAT80 定义时有效)
newtype = NPY_COMPLEX160;
break;
#endif
#ifdef NPY_FLOAT96
case 24:
// 设置新类型为复数类型 NPY_COMPLEX192(仅在 NPY_FLOAT96 定义时有效)
newtype = NPY_COMPLEX192;
break;
#endif
#ifdef NPY_FLOAT128
case 32:
// 设置新类型为复数类型 NPY_COMPLEX256(仅在 NPY_FLOAT128 定义时有效)
newtype = NPY_COMPLEX256;
break;
#endif
}
break;
case NPY_OBJECTLTR:
/*
* 对于 'O4' 和 'O8',允许通过,但会发出废弃警告。
* 对于所有其他情况,通过不设置 newtype 来引发异常。
*/
if (itemsize == 4 || itemsize == 8) {
int ret = 0;
if (evil_global_disable_warn_O4O8_flag) {
/* 2012-02-04, 1.7, 不确定何时可以移除此代码 */
// 发出废弃警告,提示使用 'O' 替代 'O4' 和 'O8'
ret = DEPRECATE("DType strings 'O4' and 'O8' are "
"deprecated because they are platform "
"specific. Use 'O' instead");
}
if (ret == 0) {
// 设置新类型为对象类型 NPY_OBJECT
newtype = NPY_OBJECT;
}
}
break;
case NPY_STRINGLTR:
// 设置新类型为字符串类型 NPY_STRING
newtype = NPY_STRING;
break;
case NPY_DEPRECATED_STRINGLTR2:
{
/*
* 发出废弃警告,可能会引发异常,如果警告被配置为错误,不设置 newtype。
*/
// 发出废弃警告,提示在 NumPy 2.0 中 'a' 数据类型别名已被废弃,使用 'S' 别名替代
int ret = DEPRECATE("Data type alias 'a' was deprecated in NumPy 2.0. "
"Use the 'S' alias instead.");
if (ret == 0) {
// 设置新类型为字符串类型 NPY_STRING
newtype = NPY_STRING;
}
break;
}
case NPY_UNICODELTR:
// 设置新类型为Unicode类型 NPY_UNICODE
newtype = NPY_UNICODE;
break;
case NPY_VOIDLTR:
// 设置新类型为VOID类型 NPY_VOID
newtype = NPY_VOID;
break;
case NPY_DATETIMELTR:
if (itemsize == 8) {
// 设置新类型为日期时间类型 NPY_DATETIME
newtype = NPY_DATETIME;
}
break;
case NPY_TIMEDELTALTR:
if (itemsize == 8) {
// 设置新类型为时间间隔类型 NPY_TIMEDELTA
newtype = NPY_TIMEDELTA;
}
break;
}
// 返回确定的新数据类型
return newtype;
}
/* Lifted from numarray */
/* TODO: not documented */
/*NUMPY_API
PyArray_IntTupleFromIntp
*/
NPY_NO_EXPORT PyObject *
PyArray_IntTupleFromIntp(int len, npy_intp const *vals)
{
int i;
// 创建一个包含整数的元组对象
PyObject *intTuple = PyTuple_New(len);
if (!intTuple) {
// 如果创建失败,跳转到失败处理分支
goto fail;
}
// 循环遍历 vals 数组,逐个处理数组元素
for (i = 0; i < len; i++) {
// 根据 vals[i] 创建一个 Python int 对象
PyObject *o = PyArray_PyIntFromIntp(vals[i]);
// 如果创建失败,则执行失败处理逻辑
if (!o) {
// 释放已创建的 intTuple 对象
Py_DECREF(intTuple);
intTuple = NULL;
// 跳转到失败处理标签
goto fail;
}
// 将创建的 int 对象 o 放入 intTuple 元组的第 i 个位置
PyTuple_SET_ITEM(intTuple, i, o);
}
fail:
// 返回 intTuple 对象,可能为 NULL 或者包含了创建的 int 对象
return intTuple;
}
.\numpy\numpy\_core\src\multiarray\conversion_utils.h
// 定义整数转换函数,将 Python 对象转换为 PyArray_Dims 结构
NPY_NO_EXPORT int
PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq);
// 定义从 Python 整数对象转换为 npy_intp 类型的函数
NPY_NO_EXPORT int
PyArray_IntpFromPyIntConverter(PyObject *o, npy_intp *val);
// 定义可选整数转换函数,将 Python 对象转换为 PyArray_Dims 结构
NPY_NO_EXPORT int
PyArray_OptionalIntpConverter(PyObject *obj, PyArray_Dims *seq);
// 定义复制模式的枚举类型
typedef enum {
NPY_COPY_NEVER = 0,
NPY_COPY_ALWAYS = 1,
NPY_COPY_IF_NEEDED = 2,
} NPY_COPYMODE;
// 定义转换函数,将 Python 对象转换为 NPY_COPYMODE 枚举类型
NPY_NO_EXPORT int
PyArray_CopyConverter(PyObject *obj, NPY_COPYMODE *copyflag);
// 定义类型转换和复制模式的枚举类型
typedef enum {
NPY_AS_TYPE_COPY_IF_NEEDED = 0,
NPY_AS_TYPE_COPY_ALWAYS = 1,
} NPY_ASTYPECOPYMODE;
// 定义类型转换和复制模式的转换函数
NPY_NO_EXPORT int
PyArray_AsTypeCopyConverter(PyObject *obj, NPY_ASTYPECOPYMODE *copyflag);
// 定义缓冲区转换函数,将 Python 对象转换为 PyArray_Chunk 结构
NPY_NO_EXPORT int
PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf);
// 定义布尔值转换函数,将 Python 对象转换为 npy_bool 类型
NPY_NO_EXPORT int
PyArray_BoolConverter(PyObject *object, npy_bool *val);
// 定义可选布尔值转换函数,将 Python 对象转换为 int 类型
NPY_NO_EXPORT int
PyArray_OptionalBoolConverter(PyObject *object, int *val);
// 定义字节顺序转换函数,将 Python 对象转换为 char 类型
NPY_NO_EXPORT int
PyArray_ByteorderConverter(PyObject *obj, char *endian);
// 定义排序方式转换函数,将 Python 对象转换为 NPY_SORTKIND 枚举类型
NPY_NO_EXPORT int
PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind);
// 定义搜索方向转换函数,将 Python 对象转换为 void 指针类型
NPY_NO_EXPORT int
PyArray_SearchsideConverter(PyObject *obj, void *addr);
// 定义将 Python 整数对象转换为 int 类型的函数
NPY_NO_EXPORT int
PyArray_PyIntAsInt(PyObject *o);
// 定义将 Python 整数对象转换为 npy_intp 类型的函数
NPY_NO_EXPORT npy_intp
PyArray_PyIntAsIntp(PyObject *o);
// 定义从索引序列转换为 npy_intp 数组的函数
NPY_NO_EXPORT npy_intp
PyArray_IntpFromIndexSequence(PyObject *seq, npy_intp *vals, npy_intp maxvals);
// 定义从序列转换为 npy_intp 数组的函数
NPY_NO_EXPORT int
PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals);
// 定义类型字符串转换函数,接受 itemsize 和 gentype 两个整数参数
NPY_NO_EXPORT int
PyArray_TypestrConvert(int itemsize, int gentype);
// 定义将 npy_intp 类型值转换为 Python 整数对象的内联函数
static inline PyObject *
PyArray_PyIntFromIntp(npy_intp const value)
{
// 根据平台字长选择不同的 PyLong_From* 函数转换
return PyLong_FromLong((long)value);
return PyLong_FromLongLong((npy_longlong)value);
}
// 定义将 npy_intp 数组转换为 Python 元组对象的函数
NPY_NO_EXPORT PyObject *
PyArray_IntTupleFromIntp(int len, npy_intp const *vals);
// 定义相关模式转换函数,将 Python 对象转换为 NPY_CORRELATEMODE 枚举类型
NPY_NO_EXPORT int
PyArray_CorrelatemodeConverter(PyObject *object, NPY_CORRELATEMODE *val);
// 定义选择类型转换函数,将 Python 对象转换为 NPY_SELECTKIND 枚举类型
NPY_NO_EXPORT int
PyArray_SelectkindConverter(PyObject *obj, NPY_SELECTKIND *selectkind);
/*
* 将轴参数转换为长度为 ndim 的布尔标志数组,
* 每个指定的轴对应的标志为 True。
*
* 如果 obj 是 None,则所有标志设置为 True。
* 如果 obj 是元组,则元组中的每个轴设置为 True。
* 如果 obj 是整数,则只有该轴设置为 True。
*/
NPY_NO_EXPORT int
PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags);
// 设备字符串转换函数,将 Python 对象转换为 NPY_DEVICE 枚举类型
NPY_NO_EXPORT int
PyArray_DeviceConverterOptional(PyObject *object, NPY_DEVICE *device);
/**
* WARNING: This flag is a bad idea, but was the only way to both
* 1) Support unpickling legacy pickles with object types.
* 2) Deprecate (and later disable) usage of O4 and O8
*
* The key problem is that the pickled representation unpickles by
* directly calling the dtype constructor, which has no way of knowing
* that it is in an unpickle context instead of a normal context without
* evil global state like we create here.
*/
extern NPY_NO_EXPORT NPY_TLS int evil_global_disable_warn_O4O8_flag;
/*
* Convert function which replaces np._NoValue with NULL.
* As a converter returns 0 on error and 1 on success.
*/
NPY_NO_EXPORT int
_not_NoValue(PyObject *obj, PyObject **out);
注释:
.\numpy\numpy\_core\src\multiarray\convert.c
/*
* 设置 NPY_NO_DEPRECATED_API 到 NPY_API_VERSION,避免使用过时的 NumPy API
* 定义 _MULTIARRAYMODULE,用于多维数组模块
*/
/*
* 清除 PY_SSIZE_T_CLEAN 宏定义,确保使用最新的 Python 对象大小 API
* 包含 Python.h 头文件,提供 Python C API 功能
* 包含 structmember.h 头文件,用于定义 C 结构体的成员
*/
/*
* 包含 numpy 库配置相关头文件
* 包含 numpy 数组对象头文件
* 包含 numpy 数组标量头文件
*/
/*
* 包含通用功能头文件
* 包含数组对象功能头文件
* 包含构造函数功能头文件
* 包含数据类型元数据头文件
* 包含映射头文件
* 包含低级分步循环头文件
* 包含标量类型头文件
* 包含数组赋值头文件
*/
/*
* 包含类型转换功能头文件
* 包含数组强制转换功能头文件
* 包含引用计数头文件
*/
/*
* 如果定义了 HAVE_FALLOCATE 并且在 Linux 平台上
* 包含 fcntl.h 头文件,提供文件控制相关功能
*/
/*
* npy_fallocate 函数:为文件 fp 分配 nbytes 大小的磁盘空间
* 允许文件系统进行更智能的分配决策,并在空间不足时快速退出
* 返回 -1 并引发异常表示空间不足,忽略其他所有错误
*/
static int
npy_fallocate(npy_intp nbytes, FILE * fp)
{
/*
* 如果定义了 HAVE_FALLOCATE 并且在 Linux 平台上
*/
int r;
/* 对于小文件不值得进行系统调用 */
if (nbytes < 16 * 1024 * 1024) {
return 0;
}
/*
* 刷新文件流,以防在 fallocate 调用和描述符中的未写数据之间存在意外交互
*/
NPY_BEGIN_ALLOW_THREADS;
r = fallocate(fileno(fp), 1, npy_ftell(fp), nbytes);
NPY_END_ALLOW_THREADS;
/*
* 如果空间不足,则提前退出,并在写入过程中发现其他错误
*/
if (r == -1 && errno == ENOSPC) {
PyErr_Format(PyExc_OSError, "Not enough free space to write "
"%"NPY_INTP_FMT" bytes", nbytes);
return -1;
}
return 0;
}
/*
* recursive_tolist 函数:将 self 数组的子数组转换为列表
* 从数据指针 dataptr 和维度 startdim 开始,直到 self 的最后一个维度
* 返回新的引用对象
*/
static PyObject *
recursive_tolist(PyArrayObject *self, char *dataptr, int startdim)
{
npy_intp i, n, stride;
PyObject *ret, *item;
/* 基本情况 */
if (startdim >= PyArray_NDIM(self)) {
return PyArray_GETITEM(self, dataptr);
}
n = PyArray_DIM(self, startdim);
stride = PyArray_STRIDE(self, startdim);
ret = PyList_New(n);
if (ret == NULL) {
return NULL;
}
for (i = 0; i < n; ++i) {
item = recursive_tolist(self, dataptr, startdim+1);
if (item == NULL) {
Py_DECREF(ret);
return NULL;
}
PyList_SET_ITEM(ret, i, item);
dataptr += stride;
}
return ret;
}
/*
* PyArray_ToList 函数:将数组 self 转换为 Python 列表
* 调用 recursive_tolist 函数完成转换
* 返回新的 Python 对象引用
*/
NPY_NO_EXPORT PyObject *
PyArray_ToList(PyArrayObject *self)
{
return recursive_tolist(self, PyArray_DATA(self), 0);
}
/* XXX: FIXME --- add ordering argument to
Allow Fortran ordering on write
This will need the addition of a Fortran-order iterator.
*/
/* 在写入时添加排序参数
允许使用Fortran排序
这将需要添加Fortran排序迭代器。
*/
/*NUMPY_API
To File
*/
/* NUMPY_API
写入文件
*/
NPY_NO_EXPORT int
/* 不导出API */
PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format)
{
npy_intp size;
npy_intp n, n2;
size_t n3, n4;
PyArrayIterObject *it;
PyObject *obj, *strobj, *tupobj, *byteobj;
n3 = (sep ? strlen((const char *)sep) : 0);
/* 计算分隔符的长度,如果不存在则为0 */
if (n3 == 0) {
/* binary data */
/* 二进制数据 */
if (PyDataType_FLAGCHK(PyArray_DESCR(self), NPY_LIST_PICKLE)) {
PyErr_SetString(PyExc_OSError,
"cannot write object arrays to a file in binary mode");
return -1;
}
/* 如果数组描述符标志包含NPY_LIST_PICKLE,抛出异常不能以二进制模式写入对象数组 */
if (PyArray_ITEMSIZE(self) == 0) {
/* For zero-width data types there's nothing to write */
/* 对于零宽度的数据类型,没有内容需要写入 */
return 0;
}
if (npy_fallocate(PyArray_NBYTES(self), fp) != 0) {
/* 使用npy_fallocate分配内存失败时返回-1 */
return -1;
}
if (PyArray_ISCONTIGUOUS(self)) {
/* 如果数组是连续存储的 */
size = PyArray_SIZE(self);
/* 获取数组的大小 */
NPY_BEGIN_ALLOW_THREADS;
/* 开始允许多线程 */
#if defined(_WIN64)
/*
* 解决 Win64 fwrite() Bug。问题详见 gh-2256
* 本地 64 位 Windows 运行时存在此问题,上述代码也将触发 UCRT(未触发的情况也可能更精确)。
*
* 如果您修改了此代码,请运行以下测试,该测试因速度慢而已从测试套件中移除。
* 原始失败模式涉及在 tofile() 过程中的无限循环。
*
* import tempfile, numpy as np
* from numpy.testing import (assert_)
* fourgbplus = 2**32 + 2**16
* testbytes = np.arange(8, dtype=np.int8)
* n = len(testbytes)
* flike = tempfile.NamedTemporaryFile()
* f = flike.file
* np.tile(testbytes, fourgbplus // testbytes.nbytes).tofile(f)
* flike.seek(0)
* a = np.fromfile(f, dtype=np.int8)
* flike.close()
* assert_(len(a) == fourgbplus)
* # check only start and end for speed:
* assert_((a[:n] == testbytes).all())
* assert_((a[-n:] == testbytes).all())
*/
{
// 计算每次写入的最大字节数,避免 Win64 下 fwrite() 的问题
size_t maxsize = 2147483648 / (size_t)PyArray_ITEMSIZE(self);
size_t chunksize;
n = 0;
// 循环写入直到所有数据被处理完毕
while (size > 0) {
// 确定当前循环可以写入的数据块大小
chunksize = (size > maxsize) ? maxsize : size;
// 调用 fwrite() 写入数据
n2 = fwrite((const void *)
((char *)PyArray_DATA(self) + (n * PyArray_ITEMSIZE(self))),
(size_t) PyArray_ITEMSIZE(self),
chunksize, fp);
// 检查写入是否完整,若不完整则跳出循环
if (n2 < chunksize) {
break;
}
// 更新已写入数据的总量和剩余数据的大小
n += n2;
size -= chunksize;
}
// 重置 size 为数组的总大小
size = PyArray_SIZE(self);
}
#else
// 非 Win64 平台下直接调用 fwrite() 写入数据
n = fwrite((const void *)PyArray_DATA(self),
(size_t) PyArray_ITEMSIZE(self),
(size_t) size, fp);
#endif
#else
// 结束任何可能存在的线程访问
NPY_END_ALLOW_THREADS;
// 如果写入的字节数小于请求的字节数,抛出异常并返回-1
if (n < size) {
PyErr_Format(PyExc_OSError,
"%ld requested and %ld written",
(long) size, (long) n);
return -1;
}
}
else {
// 定义线程开始的宏
NPY_BEGIN_THREADS_DEF;
// 创建数组迭代器对象
it = (PyArrayIterObject *) PyArray_IterNew((PyObject *)self);
// 开始线程
NPY_BEGIN_THREADS;
// 当迭代器的索引小于其大小时循环
while (it->index < it->size) {
// 将当前迭代器位置的数据写入文件
if (fwrite((const void *)it->dataptr,
(size_t) PyArray_ITEMSIZE(self),
1, fp) < 1) {
// 结束线程
NPY_END_THREADS;
// 格式化异常信息,并返回-1
PyErr_Format(PyExc_OSError,
"problem writing element %" NPY_INTP_FMT
" to file", it->index);
Py_DECREF(it);
return -1;
}
// 移动迭代器到下一个元素
PyArray_ITER_NEXT(it);
}
// 结束线程
NPY_END_THREADS;
// 释放迭代器对象的引用
Py_DECREF(it);
}
}
else:
"""
* text data
"""
# 创建一个 PyArrayIterObject 迭代器对象,用于迭代数组 self
it = (PyArrayIterObject *) PyArray_IterNew((PyObject *)self)
# 如果定义了格式字符串 format,则计算其长度;否则长度为 0
n4 = (format ? strlen((const char *)format) : 0)
# 当迭代器的索引小于数组的大小时,执行循环
while (it->index < it->size):
"""
* This is as documented. If we have a low precision float value
* then it may convert to float64 and store unnecessary digits.
* TODO: This could be fixed, by not using `arr.item()` or using
* the array printing/formatting functionality.
"""
# 获取数组中当前迭代位置的元素对象 obj
obj = PyArray_GETITEM(self, it->dataptr)
# 如果获取失败,则释放迭代器并返回 -1
if (obj == NULL):
Py_DECREF(it)
return -1
# 如果没有指定格式字符串
if (n4 == 0):
"""
* standard writing
"""
# 将 obj 转换为字符串对象 strobj
strobj = PyObject_Str(obj)
# 释放 obj 对象的引用计数
Py_DECREF(obj)
# 如果转换失败,则释放迭代器并返回 -1
if (strobj == NULL):
Py_DECREF(it)
return -1
else:
"""
* use format string
"""
# 创建一个包含 obj 的单元素元组对象 tupobj
tupobj = PyTuple_New(1)
# 如果创建失败,则释放迭代器并返回 -1
if (tupobj == NULL):
Py_DECREF(it)
return -1
# 将 obj 设置为元组的第一个元素
PyTuple_SET_ITEM(tupobj, 0, obj)
# 根据格式字符串 format 创建 Unicode 字符串对象 obj
obj = PyUnicode_FromString((const char *)format)
# 如果创建失败,则释放 tupobj 和迭代器并返回 -1
if (obj == NULL):
Py_DECREF(tupobj)
Py_DECREF(it)
return -1
# 格式化 Unicode 字符串 obj 和 tupobj,返回格式化后的字符串 strobj
strobj = PyUnicode_Format(obj, tupobj)
# 释放 obj 和 tupobj 对象的引用计数
Py_DECREF(obj)
Py_DECREF(tupobj)
# 如果格式化失败,则释放迭代器并返回 -1
if (strobj == NULL):
Py_DECREF(it)
return -1
# 将 Unicode 字符串 strobj 转换为 ASCII 字符串 byteobj
byteobj = PyUnicode_AsASCIIString(strobj)
# 在多线程环境下允许线程,获取 byteobj 的大小 n2
NPY_BEGIN_ALLOW_THREADS
n2 = PyBytes_GET_SIZE(byteobj)
# 将 byteobj 的内容写入文件 fp
n = fwrite(PyBytes_AS_STRING(byteobj), 1, n2, fp)
NPY_END_ALLOW_THREADS
# 释放 byteobj 对象的引用计数
Py_DECREF(byteobj)
# 如果写入的字节数小于应写入的字节数 n2,则抛出异常并释放 strobj 和迭代器,返回 -1
if (n < n2):
PyErr_Format(PyExc_OSError,
"problem writing element %" NPY_INTP_FMT " to file", it->index)
Py_DECREF(strobj)
Py_DECREF(it)
return -1
# 如果不是最后一个元素,则在文件中写入分隔符 sep
if (it->index != it->size - 1):
if (fwrite(sep, 1, n3, fp) < n3):
PyErr_Format(PyExc_OSError,
"problem writing separator to file")
Py_DECREF(strobj)
Py_DECREF(it)
return -1
# 释放 strobj 对象的引用计数
Py_DECREF(strobj)
# 移动到迭代器的下一个位置
PyArray_ITER_NEXT(it)
# 释放迭代器对象 it 的引用计数
Py_DECREF(it)
# 返回 0 表示成功
return 0
/*NUMPY_API*/
NPY_NO_EXPORT PyObject *
PyArray_ToString(PyArrayObject *self, NPY_ORDER order)
{
npy_intp numbytes; // 存储数组总字节数
npy_intp i; // 循环计数器
char *dptr; // 指向目标字符串的指针
int elsize; // 数组元素大小
PyObject *ret; // 返回的Python对象
PyArrayIterObject *it; // 数组迭代器对象
if (order == NPY_ANYORDER)
order = PyArray_ISFORTRAN(self) ? NPY_FORTRANORDER : NPY_CORDER;
/* if (PyArray_TYPE(self) == NPY_OBJECT) {
PyErr_SetString(PyExc_ValueError, "a string for the data" \
"in an object array is not appropriate");
return NULL;
}
*/
// 如果数组类型为对象数组,抛出错误并返回空指针
numbytes = PyArray_NBYTES(self); // 计算数组的总字节数
// 如果数组是C连续的且按顺序为C,或者是Fortran连续的且按顺序为Fortran,直接从数组数据创建字符串对象
if ((PyArray_IS_C_CONTIGUOUS(self) && (order == NPY_CORDER))
|| (PyArray_IS_F_CONTIGUOUS(self) && (order == NPY_FORTRANORDER))) {
ret = PyBytes_FromStringAndSize(PyArray_DATA(self), (Py_ssize_t) numbytes);
}
else {
PyObject *new;
if (order == NPY_FORTRANORDER) {
/* iterators are always in C-order */
// 如果按Fortran顺序,需要先转置数组为C顺序
new = PyArray_Transpose(self, NULL);
if (new == NULL) {
return NULL;
}
}
else {
Py_INCREF(self);
new = (PyObject *)self;
}
// 创建数组迭代器
it = (PyArrayIterObject *)PyArray_IterNew(new);
Py_DECREF(new);
if (it == NULL) {
return NULL;
}
// 根据数组总字节数创建新的字符串对象
ret = PyBytes_FromStringAndSize(NULL, (Py_ssize_t) numbytes);
if (ret == NULL) {
Py_DECREF(it);
return NULL;
}
// 将数组数据复制到字符串中
dptr = PyBytes_AS_STRING(ret);
i = it->size;
elsize = PyArray_ITEMSIZE(self);
while (i--) {
memcpy(dptr, it->dataptr, elsize);
dptr += elsize;
PyArray_ITER_NEXT(it);
}
Py_DECREF(it);
}
return ret; // 返回字符串对象
}
/*NUMPY_API*/
NPY_NO_EXPORT int
PyArray_FillWithScalar(PyArrayObject *arr, PyObject *obj)
{
if (PyArray_FailUnlessWriteable(arr, "assignment destination") < 0) {
return -1;
}
/*
* If we knew that the output array has at least one element, we would
* not actually need a helping buffer, we always null it, just in case.
*
* (The longlong here should help with alignment.)
*/
// 用于存储标量值的缓冲区
npy_longlong value_buffer_stack[4] = {0};
char *value_buffer_heap = NULL;
char *value = (char *)value_buffer_stack;
PyArray_Descr *descr = PyArray_DESCR(arr);
// 如果数组元素大小超过堆栈缓冲区大小,需要分配堆内存
if ((size_t)descr->elsize > sizeof(value_buffer_stack)) {
/* We need a large temporary buffer... */
// 分配足够大的临时缓冲区
value_buffer_heap = PyObject_Calloc(1, descr->elsize);
if (value_buffer_heap == NULL) {
PyErr_NoMemory();
return -1;
}
value = value_buffer_heap;
}
// 将Python对象obj打包成数组元素
if (PyArray_Pack(descr, value, obj) < 0) {
PyMem_FREE(value_buffer_heap);
return -1;
}
/*
* There is no cast anymore, the above already coerced using scalar
* coercion rules
*/
// 不再需要强制转换,上述操作已经使用标量强制转换规则执行了
// 返回成功状态
return 0;
}
# 调用 raw_array_assign_scalar 函数执行数组的标量赋值操作,并将返回值赋给 retcode
int retcode = raw_array_assign_scalar(
PyArray_NDIM(arr), PyArray_DIMS(arr), descr,
PyArray_BYTES(arr), PyArray_STRIDES(arr),
descr, value);
# 检查数据类型 descr 是否需要引用计数检查,如果需要,则调用 PyArray_ClearBuffer 清除缓冲区
if (PyDataType_REFCHK(descr)) {
PyArray_ClearBuffer(descr, value, 0, 1, 1);
}
# 释放 value_buffer_heap 指向的内存块
PyMem_FREE(value_buffer_heap);
# 返回 retcode 作为函数的返回值
return retcode;
/*
* Internal function to fill an array with zeros.
* Used in einsum and dot, which ensures the dtype is, in some sense, numerical
* and not a str or struct
*
* dst: The destination array.
* wheremask: If non-NULL, a boolean mask specifying where to set the values.
*
* Returns 0 on success, -1 on failure.
*/
NPY_NO_EXPORT int
PyArray_AssignZero(PyArrayObject *dst,
PyArrayObject *wheremask)
{
// 初始化返回码
int retcode = 0;
// 如果目标数组是对象数组
if (PyArray_ISOBJECT(dst)) {
// 创建一个表示整数0的 Python 对象
PyObject * pZero = PyLong_FromLong(0);
// 使用 PyArray_AssignRawScalar 函数将整数0赋值给目标数组
retcode = PyArray_AssignRawScalar(dst, PyArray_DESCR(dst),
(char *)&pZero, wheremask, NPY_SAFE_CASTING);
// 释放 Python 对象 pZero 的引用计数
Py_DECREF(pZero);
}
else {
/* 创建一个原始的布尔标量,其值为 False */
// 从 NPY_BOOL 类型创建一个描述符
PyArray_Descr *bool_dtype = PyArray_DescrFromType(NPY_BOOL);
if (bool_dtype == NULL) {
return -1; // 如果创建描述符失败则返回 -1
}
npy_bool value = 0; // 初始化布尔值为 False
// 使用 PyArray_AssignRawScalar 函数将布尔值赋值给目标数组
retcode = PyArray_AssignRawScalar(dst, bool_dtype, (char *)&value,
wheremask, NPY_SAFE_CASTING);
// 释放布尔类型的描述符
Py_DECREF(bool_dtype);
}
// 返回操作结果码
return retcode;
}
/*NUMPY_API
* Copy an array.
*/
NPY_NO_EXPORT PyObject *
PyArray_NewCopy(PyArrayObject *obj, NPY_ORDER order)
{
// 定义返回的数组对象指针
PyArrayObject *ret;
// 如果传入的数组对象为空,则抛出数值错误异常并返回空指针
if (obj == NULL) {
PyErr_SetString(PyExc_ValueError,
"obj is NULL in PyArray_NewCopy");
return NULL;
}
// 使用 PyArray_NewLikeArray 函数根据原数组对象创建一个相似的新数组对象
ret = (PyArrayObject *)PyArray_NewLikeArray(obj, order, NULL, 1);
if (ret == NULL) {
return NULL; // 如果创建新数组失败则返回空指针
}
// 使用 PyArray_AssignArray 函数将原数组的数据复制到新数组中
if (PyArray_AssignArray(ret, obj, NULL, NPY_UNSAFE_CASTING) < 0) {
Py_DECREF(ret);
return NULL; // 如果复制数据失败则释放新数组并返回空指针
}
// 返回新创建的数组对象
return (PyObject *)ret;
}
/*NUMPY_API
* View
* steals a reference to type -- accepts NULL
*/
NPY_NO_EXPORT PyObject *
PyArray_View(PyArrayObject *self, PyArray_Descr *type, PyTypeObject *pytype)
{
// 定义返回的数组对象指针
PyArrayObject *ret = NULL;
PyArray_Descr *dtype;
PyTypeObject *subtype;
int flags;
// 如果传入的类型非空,则使用该类型,否则使用 self 的类型
if (pytype) {
subtype = pytype;
}
else {
subtype = Py_TYPE(self);
}
// 获取 self 的数据描述符和标志位
dtype = PyArray_DESCR(self);
flags = PyArray_FLAGS(self);
// 增加数据描述符的引用计数,并使用 PyArray_NewFromDescr_int 函数创建新的数组对象
Py_INCREF(dtype);
ret = (PyArrayObject *)PyArray_NewFromDescr_int(
subtype, dtype,
PyArray_NDIM(self), PyArray_DIMS(self), PyArray_STRIDES(self),
PyArray_DATA(self),
flags, (PyObject *)self, (PyObject *)self,
_NPY_ARRAY_ENSURE_DTYPE_IDENTITY);
if (ret == NULL) {
Py_XDECREF(type);
return NULL; // 如果创建新数组对象失败则释放 type 并返回空指针
}
// 如果传入的类型非空,则将该类型设置为新数组对象的 dtype 属性
if (type != NULL) {
if (PyObject_SetAttrString((PyObject *)ret, "dtype",
(PyObject *)type) < 0) {
Py_DECREF(ret);
Py_DECREF(type);
return NULL; // 如果设置 dtype 属性失败则释放 ret 和 type 并返回空指针
}
Py_DECREF(type);
}
// 返回新创建的数组对象
return (PyObject *)ret;
}