NumPy 源码解析(六十七)
.\numpy\numpy\_core\src\multiarray\item_selection.h
/*
* Counts the number of True values in a raw boolean array. This
* is a low-overhead function which does no heap allocations.
*
* Returns -1 on error.
*/
// 声明一个函数 count_boolean_trues,用于计算原始布尔数组中 True 的数量
NPY_NO_EXPORT npy_intp
count_boolean_trues(int ndim, char *data, npy_intp const *ashape, npy_intp const *astrides);
/*
* Gets a single item from the array, based on a single multi-index
* array of values, which must be of length PyArray_NDIM(self).
*/
// 声明一个函数 PyArray_MultiIndexGetItem,从数组中获取单个元素,基于给定的多重索引数组
NPY_NO_EXPORT PyObject *
PyArray_MultiIndexGetItem(PyArrayObject *self, const npy_intp *multi_index);
/*
* Sets a single item in the array, based on a single multi-index
* array of values, which must be of length PyArray_NDIM(self).
*
* Returns 0 on success, -1 on failure.
*/
// 声明一个函数 PyArray_MultiIndexSetItem,向数组中设置单个元素,基于给定的多重索引数组
NPY_NO_EXPORT int
PyArray_MultiIndexSetItem(PyArrayObject *self, const npy_intp *multi_index,
PyObject *obj);
.\numpy\numpy\_core\src\multiarray\iterators.c
/*
* 定义宏,禁用过时的 NumPy API 并设置为当前版本
*/
/*
* 定义宏,用于 MultiArray 模块
*/
/*
* 清除 PY_SSIZE_T_CLEAN 宏定义
*/
/*
* 包含 Python.h 头文件,这是所有 Python C API 的入口点
*/
/*
* 包含 structmember.h 头文件,用于定义结构体成员访问的宏
*/
/*
* 包含 NumPy 的 arrayobject.h 头文件,用于 NumPy 数组对象的操作
*/
/*
* 包含 NumPy 的 arrayscalars.h 头文件,定义数组标量对象的相关操作
*/
/*
* 包含 NumPy 的 npy_math.h 头文件,提供数学运算相关的宏和函数声明
*/
/*
* 包含 numpy 的配置文件 npy_config.h
*/
/*
* 包含 NumPy 的 arrayobject.h 头文件,定义数组对象的操作
*/
/*
* 包含 iterators.h 头文件,定义了与迭代器相关的操作
*/
/*
* 包含 ctors.h 头文件,用于定义构造函数和析构函数
*/
/*
* 包含 common.h 头文件,提供了通用的宏和函数定义
*/
/*
* 包含 conversion_utils.h 头文件,包含了数据类型转换的工具函数
*/
/*
* 包含 dtypemeta.h 头文件,用于处理数据类型元信息
*/
/*
* 包含 array_coercion.h 头文件,包含了数组强制转换相关的操作
*/
/*
* 包含 item_selection.h 头文件,定义了数组元素选择的操作
*/
/*
* 包含 lowlevel_strided_loops.h 头文件,提供了低级别的分块循环操作
*/
/*
* 包含 array_assign.h 头文件,定义了数组赋值的操作
*/
/*
* 定义宏 NEWAXIS_INDEX 为 -1,用于表示新轴索引
*/
/*
* 定义宏 ELLIPSIS_INDEX 为 -2,用于表示省略号索引
*/
/*
* 定义宏 SINGLE_INDEX 为 -3,用于表示单一索引
*/
/*
* 尝试将 Python 对象 o 转换为 npy_intp 类型的索引值 v,不设置异常
* 如果转换成功返回 1,否则返回 0
*/
static int
coerce_index(PyObject *o, npy_intp *v)
{
*v = PyArray_PyIntAsIntp(o);
if ((*v) == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
}
return 1;
}
/*
* 将索引元组的一个元素转换为步长和步数,返回起始索引
* 非切片操作通过 n_steps 返回 NEWAXIS_INDEX、ELLIPSIS_INDEX 或 SINGLE_INDEX
*/
NPY_NO_EXPORT npy_intp
parse_index_entry(PyObject *op, npy_intp *step_size,
npy_intp *n_steps, npy_intp max,
int axis, int check_index)
{
npy_intp i;
if (op == Py_None) {
*n_steps = NEWAXIS_INDEX;
i = 0;
}
else if (op == Py_Ellipsis) {
*n_steps = ELLIPSIS_INDEX;
i = 0;
}
else if (PySlice_Check(op)) {
npy_intp stop;
if (PySlice_GetIndicesEx(op, max, &i, &stop, step_size, n_steps) < 0) {
goto fail;
}
if (*n_steps <= 0) {
*n_steps = 0;
*step_size = 1;
i = 0;
}
}
else if (coerce_index(op, &i)) {
*n_steps = SINGLE_INDEX;
*step_size = 0;
if (check_index) {
if (check_and_adjust_index(&i, max, axis, NULL) < 0) {
goto fail;
}
}
}
else {
PyErr_SetString(PyExc_IndexError,
"each index entry must be either a "
"slice, an integer, Ellipsis, or "
"newaxis");
goto fail;
}
return i;
fail:
return -1;
}
/*
* 根据迭代器和坐标获取简单迭代器的数据指针
*/
static char*
get_ptr_simple(PyArrayIterObject* iter, const npy_intp *coordinates)
{
npy_intp i;
char *ret;
ret = PyArray_DATA(iter->ao);
for(i = 0; i < PyArray_NDIM(iter->ao); ++i) {
ret += coordinates[i] * iter->strides[i];
}
return ret;
}
/*
* This is common initialization code between PyArrayIterObject and
* PyArrayNeighborhoodIterObject
*
* Steals a reference to the array object which gets removed at deallocation,
* if the iterator is allocated statically and its dealloc not called, it
* can be thought of as borrowing the reference.
*/
NPY_NO_EXPORT void
PyArray_RawIterBaseInit(PyArrayIterObject *it, PyArrayObject *ao)
{
int nd, i;
nd = PyArray_NDIM(ao); /* 获取数组对象 ao 的维度数 */
/* The legacy iterator only supports 32 dimensions */
assert(nd <= NPY_MAXDIMS_LEGACY_ITERS); /* 断言:维度数不超过 NPY_MAXDIMS_LEGACY_ITERS */
PyArray_UpdateFlags(ao, NPY_ARRAY_C_CONTIGUOUS); /* 更新数组对象 ao 的标志位,表示其是否是 C 连续的数组 */
if (PyArray_ISCONTIGUOUS(ao)) {
it->contiguous = 1; /* 如果 ao 是 C 连续的,则设置迭代器 it 的 contiguous 属性为 1 */
}
else {
it->contiguous = 0; /* 否则设置为 0 */
}
it->ao = ao; /* 设置迭代器 it 的 ao 属性为输入的数组对象 ao */
it->size = PyArray_SIZE(ao); /* 设置迭代器 it 的 size 属性为数组 ao 的总元素数 */
it->nd_m1 = nd - 1; /* 设置迭代器 it 的 nd_m1 属性为数组 ao 的维度数减一 */
if (nd != 0) {
it->factors[nd-1] = 1; /* 初始化迭代器 it 的 factors 数组,最后一个元素为 1 */
}
for (i = 0; i < nd; i++) {
it->dims_m1[i] = PyArray_DIMS(ao)[i] - 1; /* 设置迭代器 it 的 dims_m1 数组,存储数组 ao 每个维度的上限 */
it->strides[i] = PyArray_STRIDES(ao)[i]; /* 设置迭代器 it 的 strides 数组,存储数组 ao 每个维度的步长 */
it->backstrides[i] = it->strides[i] * it->dims_m1[i]; /* 计算迭代器 it 的 backstrides 数组,表示反向步长 */
if (i > 0) {
it->factors[nd-i-1] = it->factors[nd-i] * PyArray_DIMS(ao)[nd-i]; /* 计算迭代器 it 的 factors 数组,用于索引转换 */
}
it->bounds[i][0] = 0; /* 设置迭代器 it 的 bounds 数组,表示每个维度的下限 */
it->bounds[i][1] = PyArray_DIMS(ao)[i] - 1; /* 设置迭代器 it 的 bounds 数组,表示每个维度的上限 */
it->limits[i][0] = 0; /* 设置迭代器 it 的 limits 数组,表示每个维度的限制下限 */
it->limits[i][1] = PyArray_DIMS(ao)[i] - 1; /* 设置迭代器 it 的 limits 数组,表示每个维度的限制上限 */
it->limits_sizes[i] = it->limits[i][1] - it->limits[i][0] + 1; /* 计算迭代器 it 的 limits_sizes 数组,表示限制的尺寸 */
}
it->translate = &get_ptr_simple; /* 设置迭代器 it 的 translate 属性为 get_ptr_simple 函数的指针 */
PyArray_ITER_RESET(it); /* 调用 PyArray_ITER_RESET 函数初始化迭代器 it */
return;
}
static void
array_iter_base_dealloc(PyArrayIterObject *it)
{
Py_XDECREF(it->ao); /* 释放迭代器 it 的数组对象 ao 的引用 */
}
/*NUMPY_API
* Get Iterator.
*/
NPY_NO_EXPORT PyObject *
PyArray_IterNew(PyObject *obj)
{
/*
* Note that internally PyArray_RawIterBaseInit may be called directly on a
* statically allocated PyArrayIterObject.
*/
PyArrayIterObject *it;
PyArrayObject *ao;
if (!PyArray_Check(obj)) {
PyErr_BadInternalCall(); /* 如果输入对象不是数组对象,则引发错误 */
return NULL;
}
ao = (PyArrayObject *)obj; /* 将输入对象强制转换为数组对象 ao */
if (PyArray_NDIM(ao) > NPY_MAXDIMS_LEGACY_ITERS) {
PyErr_Format(PyExc_RuntimeError,
"this function only supports up to 32 dimensions but "
"the array has %d.", PyArray_NDIM(ao)); /* 如果数组对象 ao 的维度数超过限制,引发运行时错误 */
return NULL;
}
it = (PyArrayIterObject *)PyArray_malloc(sizeof(PyArrayIterObject)); /* 分配内存以存储 PyArrayIterObject 结构 */
PyObject_Init((PyObject *)it, &PyArrayIter_Type); /* 初始化 PyObject 结构体,设置类型为 PyArrayIter_Type */
/* it = PyObject_New(PyArrayIterObject, &PyArrayIter_Type); */
if (it == NULL) {
return NULL; /* 如果内存分配失败,则返回 NULL */
}
Py_INCREF(ao); /* 增加数组对象 ao 的引用计数,因为 PyArray_RawIterBaseInit 会偷走该引用 */
PyArray_RawIterBaseInit(it, ao); /* 初始化迭代器 it,并传入数组对象 ao */
return (PyObject *)it; /* 返回初始化后的迭代器 it */
}
/*NUMPY_API
* Get Iterator broadcast to a particular shape
*/
NPY_NO_EXPORT PyObject *
PyArray_BroadcastToShape(PyObject *obj, npy_intp *dims, int nd)
{
PyArrayIterObject *it;
int i, diff, j, compat, k;
PyArrayObject *ao = (PyArrayObject *)obj;
if (PyArray_NDIM(ao) > nd) {
goto err; /* 如果输入数组对象 ao 的维度数大于指定的维度数 nd,跳转到错误处理标签 */
}
compat = 1; /* 兼容标志置为 1 */
diff = j = nd - PyArray_NDIM(ao); /* 计算输入数组对象 ao 和指定维度数 nd 的差值 */
for (i = 0; i < PyArray_NDIM(ao); i++, j++) {
if (PyArray_DIMS(ao)[i] == 1) {
continue;
}
if (PyArray_DIMS(ao)[i] != dims[j]) {
compat = 0;
break;
}
}
if (!compat) {
goto err;
}
it = (PyArrayIterObject *)PyArray_malloc(sizeof(PyArrayIterObject));
if (it == NULL) {
return NULL;
}
PyObject_Init((PyObject *)it, &PyArrayIter_Type);
PyArray_UpdateFlags(ao, NPY_ARRAY_C_CONTIGUOUS);
if (PyArray_ISCONTIGUOUS(ao)) {
it->contiguous = 1;
}
else {
it->contiguous = 0;
}
Py_INCREF(ao);
it->ao = ao;
it->size = PyArray_MultiplyList(dims, nd);
it->nd_m1 = nd - 1;
if (nd != 0) {
it->factors[nd-1] = 1;
}
for (i = 0; i < nd; i++) {
it->dims_m1[i] = dims[i] - 1;
k = i - diff;
if ((k < 0) || PyArray_DIMS(ao)[k] != dims[i]) {
it->contiguous = 0;
it->strides[i] = 0;
}
else {
it->strides[i] = PyArray_STRIDES(ao)[k];
}
it->backstrides[i] = it->strides[i] * it->dims_m1[i];
if (i > 0) {
it->factors[nd-i-1] = it->factors[nd-i] * dims[nd-i];
}
}
PyArray_ITER_RESET(it);
return (PyObject *)it;
err:
PyErr_SetString(PyExc_ValueError, "array is not broadcastable to "\
"correct shape");
return NULL;
/*NUMPY_API
* Get Iterator that iterates over all but one axis (don't use this with
* PyArray_ITER_GOTO1D). The axis will be over-written if negative
* with the axis having the smallest stride.
*/
NPY_NO_EXPORT PyObject *
PyArray_IterAllButAxis(PyObject *obj, int *inaxis)
{
PyArrayObject *arr;
PyArrayIterObject *it;
int axis;
// 检查输入参数是否为 ndarray 类型
if (!PyArray_Check(obj)) {
PyErr_SetString(PyExc_ValueError,
"Numpy IterAllButAxis requires an ndarray");
return NULL;
}
// 将输入参数转换为 PyArrayObject 类型
arr = (PyArrayObject *)obj;
// 创建一个新的迭代器对象
it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)arr);
if (it == NULL) {
return NULL;
}
// 如果数组是零维的,直接返回迭代器对象
if (PyArray_NDIM(arr)==0) {
return (PyObject *)it;
}
// 如果输入的轴数小于 0,则选择具有最小步幅的轴
if (*inaxis < 0) {
int i, minaxis = 0;
npy_intp minstride = 0;
i = 0;
// 找到第一个非零步幅的轴
while (minstride == 0 && i < PyArray_NDIM(arr)) {
minstride = PyArray_STRIDE(arr,i);
i++;
}
// 遍历数组的所有轴,找到步幅最小的轴
for (i = 1; i < PyArray_NDIM(arr); i++) {
if (PyArray_STRIDE(arr,i) > 0 &&
PyArray_STRIDE(arr, i) < minstride) {
minaxis = i;
minstride = PyArray_STRIDE(arr,i);
}
}
// 更新输入的轴参数为步幅最小的轴
*inaxis = minaxis;
}
// 获取当前要迭代的轴
axis = *inaxis;
// 设置迭代器不是连续的,以便跳过特定轴
it->contiguous = 0;
// 调整迭代器的大小,以便不迭代指定轴上的元素
if (it->size != 0) {
it->size /= PyArray_DIM(arr,axis);
}
it->dims_m1[axis] = 0;
it->backstrides[axis] = 0;
/*
* (won't fix factors so don't use
* PyArray_ITER_GOTO1D with this iterator)
*/
return (PyObject *)it;
}
/*NUMPY_API
* Adjusts previously broadcasted iterators so that the axis with
* the smallest sum of iterator strides is not iterated over.
* Returns dimension which is smallest in the range [0,multi->nd).
* A -1 is returned if multi->nd == 0.
*
* don't use with PyArray_ITER_GOTO1D because factors are not adjusted
*/
NPY_NO_EXPORT int
PyArray_RemoveSmallest(PyArrayMultiIterObject *multi)
{
PyArrayIterObject *it;
int i, j;
int axis;
npy_intp smallest;
npy_intp sumstrides[NPY_MAXDIMS];
// 如果 multi->nd 等于 0,则返回 -1
if (multi->nd == 0) {
return -1;
}
// 计算每个迭代器轴上步幅的总和
for (i = 0; i < multi->nd; i++) {
sumstrides[i] = 0;
for (j = 0; j < multi->numiter; j++) {
sumstrides[i] += multi->iters[j]->strides[i];
}
}
// 初始化最小步幅的轴为第一个轴
axis = 0;
smallest = sumstrides[0];
// 找到步幅最小的轴
for (i = 1; i < multi->nd; i++) {
if (sumstrides[i] < smallest) {
axis = i;
smallest = sumstrides[i];
}
}
// 调整每个迭代器,使其不迭代最小步幅的轴
for(i = 0; i < multi->numiter; i++) {
it = multi->iters[i];
it->contiguous = 0;
if (it->size != 0) {
it->size /= (it->dims_m1[axis]+1);
}
it->dims_m1[axis] = 0;
it->backstrides[axis] = 0;
}
// 更新 multi 对象的大小为第一个迭代器的大小
multi->size = multi->iters[0]->size;
return axis;
}
static void
arrayiter_dealloc(PyArrayIterObject *it)
{
/*
* 注意:可以静态分配 PyArrayIterObject,这种情况下不会调用此函数。
*/
// 调用基类的销毁函数
array_iter_base_dealloc(it);
// 释放 PyArrayIterObject 对象
PyArray_free(it);
}
static Py_ssize_t
iter_length(PyArrayIterObject *self)
{
// 返回迭代器中的元素数量
return self->size;
}
static PyArrayObject *
iter_subscript_Bool(PyArrayIterObject *self, PyArrayObject *ind,
NPY_cast_info *cast_info)
{
npy_intp counter, strides;
int itemsize;
npy_intp count = 0;
char *dptr, *optr;
PyArrayObject *ret;
if (PyArray_NDIM(ind) != 1) {
// 如果布尔索引数组不是一维的,抛出 ValueError 异常
PyErr_SetString(PyExc_ValueError,
"boolean index array should have 1 dimension");
return NULL;
}
// 获取布尔索引数组的大小
counter = PyArray_DIMS(ind)[0];
if (counter > self->size) {
// 如果布尔索引数量超过迭代器的元素数量,抛出 ValueError 异常
PyErr_SetString(PyExc_ValueError,
"too many boolean indices");
return NULL;
}
// 获取布尔索引数组的步长
strides = PyArray_STRIDES(ind)[0];
/* 获取返回数组的大小 */
// 计算布尔真值的数量
count = count_boolean_trues(PyArray_NDIM(ind), PyArray_DATA(ind),
PyArray_DIMS(ind), PyArray_STRIDES(ind));
// 获取迭代器的元素大小
itemsize = PyArray_ITEMSIZE(self->ao);
// 复制迭代器的描述符
PyArray_Descr *dtype = PyArray_DESCR(self->ao);
Py_INCREF(dtype);
// 从描述符创建新的数组对象
ret = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(self->ao),
dtype, 1, &count,
NULL, NULL,
0, (PyObject *)self->ao);
if (ret == NULL) {
return NULL;
}
if (count > 0) {
/* 设置循环 */
// 设置输出指针
optr = PyArray_DATA(ret);
// 获取布尔索引数组的大小
counter = PyArray_DIMS(ind)[0];
// 获取布尔索引数组的数据指针
dptr = PyArray_DATA(ind);
npy_intp one = 1;
while (counter--) {
if (*((npy_bool *)dptr) != 0) {
char *args[2] = {self->dataptr, optr};
npy_intp transfer_strides[2] = {itemsize, itemsize};
// 调用类型转换函数,转换数据并写入输出数组
if (cast_info->func(&cast_info->context, args, &one,
transfer_strides, cast_info->auxdata) < 0) {
return NULL;
}
optr += itemsize;
}
// 移动布尔索引数组的数据指针
dptr += strides;
// 移动迭代器到下一个元素
PyArray_ITER_NEXT(self);
}
// 重置迭代器的位置
PyArray_ITER_RESET(self);
}
return ret;
}
static PyObject *
iter_subscript_int(PyArrayIterObject *self, PyArrayObject *ind,
NPY_cast_info *cast_info)
{
npy_intp num;
PyArrayObject *ret;
PyArrayIterObject *ind_it;
int itemsize;
char *optr;
npy_intp counter;
// 检查输入数组 `ind` 的维度是否为0
if (PyArray_NDIM(ind) == 0) {
// 如果是0维数组,获取其数据指针指向的整数值
num = *((npy_intp *)PyArray_DATA(ind));
// 检查并调整索引 `num`,确保在有效范围内
if (check_and_adjust_index(&num, self->size, -1, NULL) < 0) {
// 若索引无效,重置迭代器并返回 NULL
PyArray_ITER_RESET(self);
return NULL;
}
else {
// 否则,跳转到一维索引 `num` 处
PyObject *tmp;
PyArray_ITER_GOTO1D(self, num);
// 将该位置的数据转换为标量对象
tmp = PyArray_ToScalar(self->dataptr, self->ao);
// 重置迭代器并返回临时对象 `tmp`
PyArray_ITER_RESET(self);
return tmp;
}
}
// 获取数组 `self->ao` 的数据类型描述符并增加其引用计数
PyArray_Descr *dtype = PyArray_DESCR(self->ao);
Py_INCREF(dtype);
// 从描述符创建一个新的数组对象 `ret`
ret = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(self->ao),
dtype,
PyArray_NDIM(ind),
PyArray_DIMS(ind),
NULL, NULL,
0, (PyObject *)self->ao);
// 若创建失败,返回 NULL
if (ret == NULL) {
return NULL;
}
// 获取 `ret` 的数据指针
optr = PyArray_DATA(ret);
// 创建 `ind` 的迭代器 `ind_it`
ind_it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)ind);
// 若迭代器创建失败,释放 `ret` 并返回 NULL
if (ind_it == NULL) {
Py_DECREF(ret);
return NULL;
}
// 初始化变量
npy_intp one = 1;
itemsize = dtype->elsize;
counter = ind_it->size;
// 遍历迭代器中的每个元素
while (counter--) {
// 获取当前索引值 `num`
num = *((npy_intp *)(ind_it->dataptr));
// 检查并调整索引 `num`,确保在有效范围内
if (check_and_adjust_index(&num, self->size, -1, NULL) < 0) {
// 如果索引无效,清除 `ret` 并跳转到 `finish` 标签
Py_CLEAR(ret);
goto finish;
}
// 跳转到 `self` 的一维索引 `num` 处
PyArray_ITER_GOTO1D(self, num);
// 构建参数数组和传输步长数组,并调用 `cast_info` 中的函数进行数据类型转换
char *args[2] = {self->dataptr, optr};
npy_intp transfer_strides[2] = {itemsize, itemsize};
if (cast_info->func(&cast_info->context, args, &one,
transfer_strides, cast_info->auxdata) < 0) {
// 若转换失败,清除 `ret` 并跳转到 `finish` 标签
Py_CLEAR(ret);
goto finish;
}
// 更新 `optr` 指向下一个数据位置
optr += itemsize;
// 移动 `ind_it` 到下一个元素
PyArray_ITER_NEXT(ind_it);
}
finish:
// 释放 `ind_it` 迭代器
Py_DECREF(ind_it);
// 重置 `self` 迭代器
PyArray_ITER_RESET(self);
// 返回 `ret` 对象转换为 PyObject 类型
return (PyObject *)ret;
}
/* Always returns arrays */
NPY_NO_EXPORT PyObject *
iter_subscript(PyArrayIterObject *self, PyObject *ind)
{
PyArray_Descr *indtype = NULL; // 声明一个指向数组描述符的指针变量,初始为NULL
PyArray_Descr *dtype; // 声明一个指向数组描述符的指针变量
npy_intp start, step_size; // 声明用于存储整数索引起始位置和步长的变量
npy_intp n_steps; // 声明用于存储步数的变量
PyArrayObject *ret; // 声明一个指向数组对象的指针变量,用于存储返回结果
char *dptr; // 声明一个指向字符的指针变量
int size; // 声明一个整型变量,用于存储元素大小
PyObject *obj = NULL; // 声明一个Python对象指针变量,初始为NULL
PyObject *new; // 声明一个Python对象指针变量
NPY_cast_info cast_info = {.func = NULL}; // 声明一个结构体变量,用于存储转换函数信息,初始值为NULL
if (ind == Py_Ellipsis) {
ind = PySlice_New(NULL, NULL, NULL); // 创建一个新的切片对象,用于索引操作
obj = iter_subscript(self, ind); // 递归调用当前函数,处理切片对象作为索引
Py_DECREF(ind); // 减少切片对象的引用计数
return obj; // 返回处理结果
}
if (PyTuple_Check(ind)) {
int len; // 声明一个整型变量,用于存储元组长度
len = PyTuple_GET_SIZE(ind); // 获取元组的长度
if (len > 1) {
goto fail; // 如果元组长度大于1,则跳转到错误处理标签
}
if (len == 0) {
Py_INCREF(self->ao); // 增加数组对象的引用计数
return (PyObject *)self->ao; // 返回数组对象的Python对象指针
}
ind = PyTuple_GET_ITEM(ind, 0); // 获取元组中的第一个元素作为新的索引对象
}
/*
* Tuples >1d not accepted --- i.e. no newaxis
* Could implement this with adjusted strides and dimensions in iterator
* Check for Boolean -- this is first because Bool is a subclass of Int
*/
PyArray_ITER_RESET(self); // 重置数组迭代器的状态
if (PyBool_Check(ind)) {
int istrue = PyObject_IsTrue(ind); // 检查布尔值是否为True
if (istrue == -1) {
goto fail; // 如果检查失败,则跳转到错误处理标签
}
if (istrue) {
return PyArray_ToScalar(self->dataptr, self->ao); // 将数组中的数据转换为标量并返回
}
else { /* empty array */
npy_intp ii = 0; // 声明一个整型变量,表示空数组的长度
dtype = PyArray_DESCR(self->ao); // 获取数组对象的描述符
Py_INCREF(dtype); // 增加描述符的引用计数
ret = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(self->ao),
dtype,
1, &ii,
NULL, NULL, 0,
(PyObject *)self->ao); // 创建一个新的数组对象
return (PyObject *)ret; // 返回新创建的数组对象的Python对象指针
}
}
dtype = PyArray_DESCR(self->ao); // 获取数组对象的描述符
size = dtype->elsize; // 获取数组元素的大小
/* set up a cast to handle item copying */
NPY_ARRAYMETHOD_FLAGS transfer_flags = 0; // 声明一个标志位变量,用于存储数组操作的标志
npy_intp one = 1; // 声明一个整型变量,表示步长为1
/* We can assume the newly allocated output array is aligned */
int is_aligned = IsUintAligned(self->ao); // 检查数组是否按照整数对齐
if (PyArray_GetDTypeTransferFunction(
is_aligned, size, size, dtype, dtype, 0, &cast_info,
&transfer_flags) < 0) {
goto fail; // 如果获取数据类型转换函数失败,则跳转到错误处理标签
}
/* Check for Integer or Slice */
if (PyLong_Check(ind) || PySlice_Check(ind)) {
start = parse_index_entry(ind, &step_size, &n_steps,
self->size, 0, 1);
if (start == -1) {
goto fail;
}
if (n_steps == ELLIPSIS_INDEX || n_steps == NEWAXIS_INDEX) {
PyErr_SetString(PyExc_IndexError,
"cannot use Ellipsis or newaxes here");
goto fail;
}
PyArray_ITER_GOTO1D(self, start);
if (n_steps == SINGLE_INDEX) { /* Integer */
PyObject *tmp;
tmp = PyArray_ToScalar(self->dataptr, self->ao);
PyArray_ITER_RESET(self);
NPY_cast_info_xfree(&cast_info);
return tmp;
}
Py_INCREF(dtype);
ret = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(self->ao),
dtype,
1, &n_steps,
NULL, NULL,
0, (PyObject *)self->ao);
if (ret == NULL) {
goto fail;
}
dptr = PyArray_DATA(ret);
while (n_steps--) {
char *args[2] = {self->dataptr, dptr};
npy_intp transfer_strides[2] = {size, size};
if (cast_info.func(&cast_info.context, args, &one,
transfer_strides, cast_info.auxdata) < 0) {
goto fail;
}
start += step_size;
PyArray_ITER_GOTO1D(self, start);
dptr += size;
}
PyArray_ITER_RESET(self);
NPY_cast_info_xfree(&cast_info);
return (PyObject *)ret;
}
/* convert to INTP array if Integer array scalar or List */
indtype = PyArray_DescrFromType(NPY_INTP);
if (PyArray_IsScalar(ind, Integer) || PyList_Check(ind)) {
Py_INCREF(indtype);
obj = PyArray_FromAny(ind, indtype, 0, 0, NPY_ARRAY_FORCECAST, NULL);
if (obj == NULL) {
goto fail;
}
}
else {
Py_INCREF(ind);
obj = ind;
}
/* Any remaining valid input is an array or has been turned into one */
if (!PyArray_Check(obj)) {
goto fail;
}
/* Check for Boolean array */
if (PyArray_TYPE((PyArrayObject *)obj) == NPY_BOOL) {
ret = iter_subscript_Bool(self, (PyArrayObject *)obj, &cast_info);
goto finish;
}
/* Only integer arrays left */
if (!PyArray_ISINTEGER((PyArrayObject *)obj)) {
goto fail;
}
Py_INCREF(indtype);
new = PyArray_FromAny(obj, indtype, 0, 0,
NPY_ARRAY_FORCECAST | NPY_ARRAY_ALIGNED, NULL);
if (new == NULL) {
goto fail;
}
ret = (PyArrayObject *)iter_subscript_int(self, (PyArrayObject *)new,
&cast_info);
Py_DECREF(new);
finish:
Py_DECREF(indtype);
Py_DECREF(obj);
NPY_cast_info_xfree(&cast_info);
return (PyObject *)ret;
fail:
// 检查是否存在 Python 异常状态
if (!PyErr_Occurred()) {
// 如果没有异常,设置一个索引错误异常并设定错误消息
PyErr_SetString(PyExc_IndexError, "unsupported iterator index");
}
// 释放 Python 对象引用,避免内存泄漏
Py_XDECREF(indtype);
Py_XDECREF(obj);
// 释放 NumPy 的类型转换信息资源,包括内存空间的释放
NPY_cast_info_xfree(&cast_info);
// 返回 NULL 表示函数执行失败
return NULL;
static`
static int
iter_ass_sub_Bool(PyArrayIterObject *self, PyArrayObject *ind,
PyArrayIterObject *val, NPY_cast_info *cast_info)
{
npy_intp counter, strides; // 定义计数器和步长变量
char *dptr; // 定义指向布尔索引数组数据的指针
if (PyArray_NDIM(ind) != 1) { // 检查布尔索引数组的维度是否为1
PyErr_SetString(PyExc_ValueError,
"boolean index array should have 1 dimension"); // 抛出错误信息
return -1; // 返回-1,表示出错
}
counter = PyArray_DIMS(ind)[0]; // 获取布尔索引数组的长度
if (counter > self->size) { // 检查布尔索引数组长度是否超过迭代器的大小
PyErr_SetString(PyExc_ValueError,
"boolean index array has too many values"); // 抛出错误信息
return -1; // 返回-1,表示出错
}
strides = PyArray_STRIDES(ind)[0]; // 获取布尔索引数组的步长
dptr = PyArray_DATA(ind); // 获取布尔索引数组的数据指针
PyArray_ITER_RESET(self); // 重置迭代器的状态
/* Loop over Boolean array */ // 循环遍历布尔数组
npy_intp one = 1; // 定义一个大小为1的整数
PyArray_Descr *dtype = PyArray_DESCR(self->ao); // 获取迭代器所操作数组的数据类型描述符
int itemsize = dtype->elsize; // 获取数组元素的大小
npy_intp transfer_strides[2] = {itemsize, itemsize}; // 定义数据传输的步长数组
while (counter--) { // 循环,直到计数器减为0
if (*((npy_bool *)dptr) != 0) { // 如果布尔数组当前位置的值不为0
char *args[2] = {val->dataptr, self->dataptr}; // 创建一个参数数组,包含值迭代器和目标迭代器的数据指针
if (cast_info->func(&cast_info->context, args, &one,
transfer_strides, cast_info->auxdata) < 0) {
return -1; // 如果类型转换函数返回小于0的值,表示出错,返回-1
}
PyArray_ITER_NEXT(val); // 移动值迭代器到下一个位置
if (val->index == val->size) { // 如果值迭代器的索引等于其大小
PyArray_ITER_RESET(val); // 重置值迭代器的状态
}
}
dptr += strides; // 移动布尔索引数组数据指针到下一个位置
PyArray_ITER_NEXT(self); // 移动迭代器到下一个位置
}
PyArray_ITER_RESET(self); // 最后重置迭代器的状态
return 0; // 返回0,表示操作成功完成
}
while (counter--) {
num = *((npy_intp *)(ind_it->dataptr));
if (check_and_adjust_index(&num, self->size, -1, NULL) < 0) {
Py_DECREF(ind_it);
return -1;
}
PyArray_ITER_GOTO1D(self, num);
char *args[2] = {val->dataptr, self->dataptr};
if (cast_info->func(&cast_info->context, args, &one,
transfer_strides, cast_info->auxdata) < 0) {
Py_DECREF(ind_it);
return -1;
}
PyArray_ITER_NEXT(ind_it);
PyArray_ITER_NEXT(val);
if (val->index == val->size) {
PyArray_ITER_RESET(val);
}
}
Py_DECREF(ind_it);
return 0;
/*
* 以下是iter_ass_subscript函数的具体实现,用于处理NumPy数组迭代器的元素赋值操作。
* 根据ind参数类型的不同,实现不同的赋值逻辑。
*/
NPY_NO_EXPORT int
iter_ass_subscript(PyArrayIterObject *self, PyObject *ind, PyObject *val)
{
PyArrayObject *arrval = NULL;
PyArrayIterObject *val_it = NULL;
PyArray_Descr *type;
PyArray_Descr *indtype = NULL;
int retval = -1;
npy_intp start, step_size;
npy_intp n_steps;
PyObject *obj = NULL;
NPY_cast_info cast_info = {.func = NULL};
// 如果val为NULL,无法删除迭代器元素,抛出TypeError异常
if (val == NULL) {
PyErr_SetString(PyExc_TypeError,
"Cannot delete iterator elements");
return -1;
}
// 确保底层数组可写
if (PyArray_FailUnlessWriteable(self->ao, "underlying array") < 0)
return -1;
// 处理ind为省略号(Ellipsis)的情况,递归调用自身处理
if (ind == Py_Ellipsis) {
ind = PySlice_New(NULL, NULL, NULL);
retval = iter_ass_subscript(self, ind, val);
Py_DECREF(ind);
return retval;
}
// 处理ind为元组的情况,只取第一个元素处理
if (PyTuple_Check(ind)) {
int len;
len = PyTuple_GET_SIZE(ind);
if (len > 1) {
goto finish;
}
ind = PyTuple_GET_ITEM(ind, 0);
}
// 获取底层数组的描述符
type = PyArray_DESCR(self->ao);
/*
* 检查是否为布尔类型 -- 这是因为
* 布尔类型是整数类型的子类
*/
if (PyBool_Check(ind)) {
retval = 0;
int istrue = PyObject_IsTrue(ind);
if (istrue == -1) {
return -1;
}
// 如果布尔值为真,则将val打包到数组中
if (istrue) {
retval = PyArray_Pack(
PyArray_DESCR(self->ao), self->dataptr, val);
}
goto finish;
}
// 如果ind为序列或切片对象,则跳过后续处理
if (PySequence_Check(ind) || PySlice_Check(ind)) {
goto skip;
}
// 尝试将ind转换为npy_intp类型作为起始索引
start = PyArray_PyIntAsIntp(ind);
if (error_converting(start)) {
PyErr_Clear();
}
else {
// 检查并调整索引,然后定位到一维位置
if (check_and_adjust_index(&start, self->size, -1, NULL) < 0) {
goto finish;
}
PyArray_ITER_GOTO1D(self, start);
// 将val打包到数组的当前位置
retval = PyArray_Pack(PyArray_DESCR(self->ao), self->dataptr, val);
PyArray_ITER_RESET(self);
if (retval < 0) {
PyErr_SetString(PyExc_ValueError,
"Error setting single item of array.");
}
goto finish;
}
skip:
// 增加对类型的引用计数,并尝试从val创建一个PyArrayObject对象
Py_INCREF(type);
arrval = (PyArrayObject *)PyArray_FromAny(val, type, 0, 0,
NPY_ARRAY_FORCECAST, NULL);
if (arrval == NULL) {
return -1;
}
// 创建val的迭代器对象
val_it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)arrval);
if (val_it == NULL) {
goto finish;
}
// 如果val_it的大小为0,则直接返回0
if (val_it->size == 0) {
retval = 0;
goto finish;
}
/* 设置转换以处理将单个元素复制到arrval中 */
NPY_ARRAYMETHOD_FLAGS transfer_flags = 0;
npy_intp one = 1;
int itemsize = type->elsize;
/* 我们可以假设新分配的数组是对齐的 */
int is_aligned = IsUintAligned(self->ao);
// 获取数据类型转换函数
if (PyArray_GetDTypeTransferFunction(
is_aligned, itemsize, itemsize, type, type, 0,
&cast_info, &transfer_flags) < 0) {
goto finish;
}
/* 检查切片 */
if (PySlice_Check(ind)) {
start = parse_index_entry(ind, &step_size, &n_steps, self->size, 0, 0);
if (start == -1) {
goto finish;
}
if (n_steps == ELLIPSIS_INDEX || n_steps == NEWAXIS_INDEX) {
PyErr_SetString(PyExc_IndexError,
"cannot use Ellipsis or newaxes here");
goto finish;
}
PyArray_ITER_GOTO1D(self, start);
npy_intp transfer_strides[2] = {itemsize, itemsize};
if (n_steps == SINGLE_INDEX) {
char *args[2] = {PyArray_DATA(arrval), self->dataptr};
if (cast_info.func(&cast_info.context, args, &one,
transfer_strides, cast_info.auxdata) < 0) {
goto finish;
}
PyArray_ITER_RESET(self);
retval = 0;
goto finish;
}
while (n_steps--) {
char *args[2] = {val_it->dataptr, self->dataptr};
if (cast_info.func(&cast_info.context, args, &one,
transfer_strides, cast_info.auxdata) < 0) {
goto finish;
}
start += step_size;
PyArray_ITER_GOTO1D(self, start);
PyArray_ITER_NEXT(val_it);
if (val_it->index == val_it->size) {
PyArray_ITER_RESET(val_it);
}
}
PyArray_ITER_RESET(self);
retval = 0;
goto finish;
}
/* 将对象转换为 INTP 数组,如果是整数数组标量或列表 */
indtype = PyArray_DescrFromType(NPY_INTP);
if (PyList_Check(ind)) {
Py_INCREF(indtype);
obj = PyArray_FromAny(ind, indtype, 0, 0, NPY_ARRAY_FORCECAST, NULL);
}
else {
Py_INCREF(ind);
obj = ind;
}
if (obj != NULL && PyArray_Check(obj)) {
/* 检查是否为布尔数组 */
if (PyArray_TYPE((PyArrayObject *)obj)==NPY_BOOL) {
if (iter_ass_sub_Bool(self, (PyArrayObject *)obj,
val_it, &cast_info) < 0) {
goto finish;
}
retval=0;
}
/* 检查是否为整数数组 */
else if (PyArray_ISINTEGER((PyArrayObject *)obj)) {
PyObject *new;
Py_INCREF(indtype);
new = PyArray_CheckFromAny(obj, indtype, 0, 0,
NPY_ARRAY_FORCECAST | NPY_ARRAY_BEHAVED_NS, NULL);
Py_DECREF(obj);
obj = new;
if (new == NULL) {
goto finish;
}
if (iter_ass_sub_int(self, (PyArrayObject *)obj,
val_it, &cast_info) < 0) {
goto finish;
}
retval = 0;
}
}
finish:
if (!PyErr_Occurred() && retval < 0) {
PyErr_SetString(PyExc_IndexError, "unsupported iterator index");
}
Py_XDECREF(indtype);
Py_XDECREF(obj);
Py_XDECREF(val_it);
Py_XDECREF(arrval);
NPY_cast_info_xfree(&cast_info);
return retval;
}
static PyMappingMethods iter_as_mapping = {
(lenfunc)iter_length, /* mp_length */
(binaryfunc)iter_subscript, /* mp_subscript */
(objobjargproc)iter_ass_subscript, /* mp_ass_subscript */
};
/* Two options:
* 1) underlying array is contiguous
* -- return 1-d wrapper around it
* 2) underlying array is not contiguous
* -- make new 1-d contiguous array with updateifcopy flag set
* to copy back to the old array
*
* If underlying array is readonly, then we make the output array readonly
* and updateifcopy does not apply.
*
* Changed 2017-07-21, 1.14.0.
*
* In order to start the process of removing UPDATEIFCOPY, see gh-7054, the
* behavior is changed to always return an non-writeable copy when the base
* array is non-contiguous. Doing that will hopefully smoke out those few
* folks who assign to the result with the expectation that the base array
* will be changed. At a later date non-contiguous arrays will always return
* writeable copies.
*
* Note that the type and argument expected for the __array__ method is
* ignored.
*/
static PyArrayObject *
iter_array(PyArrayIterObject *it, PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(kwds))
{
PyArrayObject *ret;
npy_intp size;
size = PyArray_SIZE(it->ao); // 获取数组对象 `it` 的大小
Py_INCREF(PyArray_DESCR(it->ao)); // 增加数组描述符的引用计数
if (PyArray_ISCONTIGUOUS(it->ao)) {
// 如果数组是连续的,则创建一个基于原数组的一维包装器
ret = (PyArrayObject *)PyArray_NewFromDescrAndBase(
&PyArray_Type, PyArray_DESCR(it->ao),
1, &size, NULL, PyArray_DATA(it->ao),
PyArray_FLAGS(it->ao), (PyObject *)it->ao, (PyObject *)it->ao);
if (ret == NULL) {
return NULL;
}
}
else {
// 如果数组不是连续的,则创建一个新的一维连续数组,并设置 updateifcopy 标志
ret = (PyArrayObject *)PyArray_NewFromDescr(
&PyArray_Type, PyArray_DESCR(it->ao), 1, &size,
NULL, NULL, 0, (PyObject *)it->ao);
if (ret == NULL) {
return NULL;
}
if (PyArray_CopyAnyInto(ret, it->ao) < 0) {
Py_DECREF(ret);
return NULL;
}
// 清除可写标志,使得输出数组为只读状态
PyArray_CLEARFLAGS(ret, NPY_ARRAY_WRITEABLE);
}
return ret; // 返回创建的数组对象
}
static PyObject *
iter_copy(PyArrayIterObject *it, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
// 返回数组的扁平化视图
return PyArray_Flatten(it->ao, 0);
}
static PyMethodDef iter_methods[] = {
/* to get array */
{"__array__",
(PyCFunction)iter_array, // 对应的 C 函数为 iter_array
METH_VARARGS | METH_KEYWORDS, NULL}, // 方法接受位置参数和关键字参数
{"copy",
(PyCFunction)iter_copy, // 对应的 C 函数为 iter_copy
METH_VARARGS, NULL}, // 方法接受位置参数
{NULL, NULL, 0, NULL} /* sentinel */
};
static PyObject *
iter_richcompare(PyArrayIterObject *self, PyObject *other, int cmp_op)
{
PyArrayObject *new;
PyObject *ret;
new = (PyArrayObject *)iter_array(self, NULL, NULL); // 获取数组的新视图
if (new == NULL) {
return NULL;
}
// 使用数组的比较方法进行比较
ret = array_richcompare(new, other, cmp_op);
// 解析写回复制标志
PyArray_ResolveWritebackIfCopy(new);
Py_DECREF(new); // 减少数组视图的引用计数
// 返回比较的结果
return ret;
}
return ret;
/** END of Array Iterator **/
// 设置迭代器成员变量的定义
static PyMemberDef iter_members[] = {
// base 成员,对象类型,偏移量,只读,无特殊处理函数
{"base",
T_OBJECT,
offsetof(PyArrayIterObject, ao),
READONLY, NULL},
// 数组结束标志
{NULL, 0, 0, 0, NULL},
};
// 定义获取迭代器索引的函数
static PyObject *
iter_index_get(PyArrayIterObject *self, void *NPY_UNUSED(ignored))
{
// 返回迭代器当前的索引值
return PyArray_PyIntFromIntp(self->index);
}
// 定义获取迭代器坐标的函数
static PyObject *
iter_coords_get(PyArrayIterObject *self, void *NPY_UNUSED(ignored))
{
int nd;
nd = PyArray_NDIM(self->ao);
// 如果数组是连续存储的
if (self->contiguous) {
/*
* 坐标不被跟踪 ---
* 需要根据索引生成
*/
npy_intp val;
int i;
val = self->index;
for (i = 0; i < nd; i++) {
// 如果因子不为零,计算坐标
if (self->factors[i] != 0) {
self->coordinates[i] = val / self->factors[i];
val = val % self->factors[i];
} else {
self->coordinates[i] = 0;
}
}
}
// 返回坐标的整数元组对象
return PyArray_IntTupleFromIntp(nd, self->coordinates);
}
// 定义获取器的获取和设置函数
static PyGetSetDef iter_getsets[] = {
// 获取索引的属性
{"index",
(getter)iter_index_get,
NULL, NULL, NULL},
// 获取坐标的属性
{"coords",
(getter)iter_coords_get,
NULL, NULL, NULL},
// 结束标志
{NULL, NULL, NULL, NULL, NULL},
};
// 定义数组迭代器类型对象
NPY_NO_EXPORT PyTypeObject PyArrayIter_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
// 对象类型名
.tp_name = "numpy.flatiter",
// 基本大小
.tp_basicsize = sizeof(PyArrayIterObject),
// 析构函数
.tp_dealloc = (destructor)arrayiter_dealloc,
// 映射接口
.tp_as_mapping = &iter_as_mapping,
// 标志
.tp_flags = Py_TPFLAGS_DEFAULT,
// 富比较函数
.tp_richcompare = (richcmpfunc)iter_richcompare,
// 迭代下一个函数
.tp_iternext = (iternextfunc)arrayiter_next,
// 方法
.tp_methods = iter_methods,
// 成员变量
.tp_members = iter_members,
// 获取器
.tp_getset = iter_getsets,
};
// 调整索引对象迭代器的维度和步长
/*NUMPY_API*/
NPY_NO_EXPORT int
PyArray_Broadcast(PyArrayMultiIterObject *mit)
{
int i, nd, k, j;
int src_iter = -1; /* Initializing avoids a compiler warning. */
npy_intp tmp;
PyArrayIterObject *it;
// 发现广播的维度数量
/* 遍历所有迭代器,确定迭代器中最大的维度数 */
for (i = 0, nd = 0; i < mit->numiter; i++) {
nd = PyArray_MAX(nd, PyArray_NDIM(mit->iters[i]->ao));
}
mit->nd = nd;
/* 在每个维度中发现广播形状 */
for (i = 0; i < nd; i++) {
mit->dimensions[i] = 1;
for (j = 0; j < mit->numiter; j++) {
it = mit->iters[j];
/* 如果数组的维度小于最大维度nd,就在其前面添加1 */
k = i + PyArray_NDIM(it->ao) - nd;
if (k >= 0) {
tmp = PyArray_DIMS(it->ao)[k];
if (tmp == 1) {
continue;
}
/* 如果mit->dimensions[i]为1,将其设置为tmp */
if (mit->dimensions[i] == 1) {
mit->dimensions[i] = tmp;
src_iter = j;
}
/* 如果mit->dimensions[i]不等于tmp,则设置形状不匹配的异常 */
else if (mit->dimensions[i] != tmp) {
set_shape_mismatch_exception(mit, src_iter, j);
return -1;
}
}
}
}
/*
* 重设每个迭代器对象的迭代器维度和步长 -- 使用值为0的步长进行广播
* 需要检查溢出
*/
tmp = PyArray_OverflowMultiplyList(mit->dimensions, mit->nd);
if (tmp < 0) {
PyErr_SetString(PyExc_ValueError,
"broadcast dimensions too large.");
return -1;
}
mit->size = tmp;
for (i = 0; i < mit->numiter; i++) {
it = mit->iters[i];
it->nd_m1 = mit->nd - 1;
it->size = tmp;
nd = PyArray_NDIM(it->ao);
if (nd != 0) {
it->factors[mit->nd-1] = 1;
}
for (j = 0; j < mit->nd; j++) {
it->dims_m1[j] = mit->dimensions[j] - 1;
k = j + nd - mit->nd;
/*
* 如果此维度是添加的或底层数组的形状为1
*/
if ((k < 0) ||
PyArray_DIMS(it->ao)[k] != mit->dimensions[j]) {
it->contiguous = 0;
it->strides[j] = 0;
}
else {
it->strides[j] = PyArray_STRIDES(it->ao)[k];
}
it->backstrides[j] = it->strides[j] * it->dims_m1[j];
if (j > 0)
it->factors[mit->nd-j-1] =
it->factors[mit->nd-j] * mit->dimensions[mit->nd-j];
}
PyArray_ITER_RESET(it);
}
return 0;
/*
* 返回一个错误信息对象,指示传递的参数数量不正确。
* 参数数量需要至少为0且最多为NPY_MAXARGS个数组对象。
*/
static inline PyObject*
multiiter_wrong_number_of_args(void)
{
return PyErr_Format(PyExc_ValueError,
"Need at least 0 and at most %d "
"array objects.", NPY_MAXARGS);
}
/*
* PyArrayMultiIterObject 构造函数的通用实现。
* 创建一个 PyArrayMultiIterObject 对象,用于迭代多个数组。
*/
static PyObject*
multiiter_new_impl(int n_args, PyObject **args)
{
PyArrayMultiIterObject *multi;
int i;
// 分配 PyArrayMultiIterObject 结构的内存空间
multi = PyArray_malloc(sizeof(PyArrayMultiIterObject));
if (multi == NULL) {
return PyErr_NoMemory();
}
// 初始化 multi 对象,设置其类型为 PyArrayMultiIter_Type
PyObject_Init((PyObject *)multi, &PyArrayMultiIter_Type);
multi->numiter = 0;
for (i = 0; i < n_args; ++i) {
PyObject *obj = args[i];
PyObject *arr;
PyArrayIterObject *it;
// 如果 obj 是 PyArrayMultiIterObject 类型的实例
if (PyObject_IsInstance(obj, (PyObject *)&PyArrayMultiIter_Type)) {
PyArrayMultiIterObject *mit = (PyArrayMultiIterObject *)obj;
int j;
// 检查将要添加的迭代器数量是否超过 NPY_MAXARGS
if (multi->numiter + mit->numiter > NPY_MAXARGS) {
multiiter_wrong_number_of_args();
goto fail;
}
// 将 mit 中的每个迭代器添加到 multi 中
for (j = 0; j < mit->numiter; ++j) {
arr = (PyObject *)mit->iters[j]->ao;
it = (PyArrayIterObject *)PyArray_IterNew(arr);
if (it == NULL) {
goto fail;
}
multi->iters[multi->numiter++] = it;
}
}
// 如果 multi 中迭代器的数量还未达到 NPY_MAXARGS,且 obj 可以转换为数组对象
else if (multi->numiter < NPY_MAXARGS) {
arr = PyArray_FromAny(obj, NULL, 0, 0, 0, NULL);
if (arr == NULL) {
goto fail;
}
it = (PyArrayIterObject *)PyArray_IterNew(arr);
Py_DECREF(arr);
if (it == NULL) {
goto fail;
}
multi->iters[multi->numiter++] = it;
}
else {
multiiter_wrong_number_of_args();
goto fail;
}
}
// 检查是否成功添加了迭代器
if (multi->numiter < 0) {
multiiter_wrong_number_of_args();
goto fail;
}
// 对 multi 进行广播处理
if (PyArray_Broadcast(multi) < 0) {
goto fail;
}
// 重置 multi 的多重迭代器状态
PyArray_MultiIter_RESET(multi);
return (PyObject *)multi;
fail:
// 在失败时释放 multi 对象
Py_DECREF(multi);
return NULL;
}
/*NUMPY_API
* 从 Python 对象数组和任何额外数组中获取 MultiIterator
*
* PyObject **mps - PyObjects 数组
* int n - 数组中的 PyObjects 数量
* int nadd - 要包含在迭代器中的额外数组数量
*
* 返回一个 MultiIterator 对象。
*/
NPY_NO_EXPORT PyObject*
PyArray_MultiIterFromObjects(PyObject **mps, int n, int nadd, ...)
{
PyObject *args_impl[NPY_MAXARGS];
int ntot = n + nadd;
int i;
va_list va;
// 检查总参数数量是否在合理范围内
if ((ntot > NPY_MAXARGS) || (ntot < 0)) {
return multiiter_wrong_number_of_args();
}
// 复制 mps 数组中的 Python 对象到 args_impl 中
for (i = 0; i < n; ++i) {
args_impl[i] = mps[i];
}
// 处理可变参数列表,将其添加到 args_impl 中
va_start(va, nadd);
for (; i < ntot; ++i) {
args_impl[i] = va_arg(va, PyObject *);
}
va_end(va);
// 调用 multiiter_new_impl 函数创建 MultiIterator 对象并返回
return multiiter_new_impl(ntot, args_impl);
}
/*NUMPY_API
* Get MultiIterator,
*/
/* 定义 PyArray_MultiIterNew 函数,返回 PyObject 指针类型 */
NPY_NO_EXPORT PyObject*
PyArray_MultiIterNew(int n, ...)
{
/* 定义存储 PyObject 指针的数组 */
PyObject *args_impl[NPY_MAXARGS];
/* 定义循环计数器 */
int i;
/* 定义 va_list 变量 */
va_list va;
/* 如果参数个数超过 NPY_MAXARGS 或小于 0,则调用 multiiter_wrong_number_of_args 函数返回错误信息 */
if ((n > NPY_MAXARGS) || (n < 0)) {
return multiiter_wrong_number_of_args();
}
/* 开始使用可变参数列表 */
va_start(va, n);
/* 将可变参数列表中的参数依次存入 args_impl 数组中 */
for (i = 0; i < n; ++i) {
args_impl[i] = va_arg(va, PyObject *);
}
/* 结束可变参数列表的使用 */
va_end(va);
/* 调用 multiiter_new_impl 函数,返回多迭代器对象的 PyObject 指针 */
return multiiter_new_impl(n, args_impl);
}
/* 定义 arraymultiter_new 函数 */
static PyObject*
arraymultiter_new(PyTypeObject *NPY_UNUSED(subtype), PyObject *args,
PyObject *kwds)
{
/* 定义返回值、快速序列和序列长度 */
PyObject *ret, *fast_seq;
Py_ssize_t n;
/* 如果关键字参数不为空且大于 0,抛出 ValueError 异常 */
if (kwds != NULL && PyDict_Size(kwds) > 0) {
PyErr_SetString(PyExc_ValueError,
"keyword arguments not accepted.");
return NULL;
}
/* 将传入的参数 args 转换为快速序列 fast_seq */
fast_seq = PySequence_Fast(args, ""); // needed for pypy
/* 如果转换失败,返回 NULL */
if (fast_seq == NULL) {
return NULL;
}
/* 获取快速序列的长度 */
n = PySequence_Fast_GET_SIZE(fast_seq);
/* 如果长度大于 NPY_MAXARGS,释放 fast_seq 并返回错误信息 */
if (n > NPY_MAXARGS) {
Py_DECREF(fast_seq);
return multiiter_wrong_number_of_args();
}
/* 调用 multiiter_new_impl 函数,返回多迭代器对象的 PyObject 指针 */
ret = multiiter_new_impl(n, PySequence_Fast_ITEMS(fast_seq));
/* 释放 fast_seq 对象 */
Py_DECREF(fast_seq);
return ret;
}
/* 定义 arraymultiter_next 函数 */
static PyObject *
arraymultiter_next(PyArrayMultiIterObject *multi)
{
/* 定义返回值、迭代器数目和循环计数器 */
PyObject *ret;
int i, n;
/* 获取迭代器的数目 */
n = multi->numiter;
/* 创建一个元组对象作为返回值 */
ret = PyTuple_New(n);
/* 如果创建失败,返回 NULL */
if (ret == NULL) {
return NULL;
}
/* 如果 multi->index 小于 multi->size */
if (multi->index < multi->size) {
/* 遍历迭代器数组 */
for (i = 0; i < n; i++) {
/* 获取当前迭代器对象 */
PyArrayIterObject *it=multi->iters[i];
/* 将迭代器指向的数据转换为标量对象,存入元组中 */
PyTuple_SET_ITEM(ret, i,
PyArray_ToScalar(it->dataptr, it->ao));
/* 移动迭代器到下一个位置 */
PyArray_ITER_NEXT(it);
}
/* 增加 multi 的索引值 */
multi->index++;
/* 返回元组对象 */
return ret;
}
/* 如果 multi->index 大于等于 multi->size,释放 ret 对象并返回 NULL */
Py_DECREF(ret);
return NULL;
}
/* 定义 arraymultiter_dealloc 函数 */
static void
arraymultiter_dealloc(PyArrayMultiIterObject *multi)
{
/* 定义循环计数器 */
int i;
/* 释放迭代器数组中的每一个迭代器对象 */
for (i = 0; i < multi->numiter; i++) {
Py_XDECREF(multi->iters[i]);
}
/* 释放 multi 对象内存 */
Py_TYPE(multi)->tp_free((PyObject *)multi);
}
/* 定义 arraymultiter_size_get 函数 */
static PyObject *
arraymultiter_size_get(PyArrayMultiIterObject *self, void *NPY_UNUSED(ignored))
{
/* 返回 self 对象的 size 属性 */
return PyArray_PyIntFromIntp(self->size);
}
/* 定义 arraymultiter_index_get 函数 */
static PyObject *
arraymultiter_index_get(PyArrayMultiIterObject *self, void *NPY_UNUSED(ignored))
{
/* 返回 self 对象的 index 属性 */
return PyArray_PyIntFromIntp(self->index);
}
/* 定义 arraymultiter_shape_get 函数 */
static PyObject *
arraymultiter_shape_get(PyArrayMultiIterObject *self, void *NPY_UNUSED(ignored))
{
/* 返回 self 对象的 dimensions 属性 */
return PyArray_IntTupleFromIntp(self->nd, self->dimensions);
}
/* 定义 arraymultiter_iters_get 函数 */
static PyObject *
arraymultiter_iters_get(PyArrayMultiIterObject *self, void *NPY_UNUSED(ignored))
{
/* 返回一个元组对象,包含 self 对象的所有迭代器 */
PyObject *res;
int i, n;
/* 获取迭代器的数目 */
n = self->numiter;
/* 创建一个包含 n 个元素的元组对象 */
res = PyTuple_New(n);
/* 如果创建失败,返回 NULL */
if (res == NULL) {
return res;
}
/* 遍历 self 对象的迭代器数组,将每个迭代器对象添加到元组中 */
for (i = 0; i < n; i++) {
Py_INCREF(self->iters[i]);
PyTuple_SET_ITEM(res, i, (PyObject *)self->iters[i]);
}
/* 返回包含迭代器的元组对象 */
return res;
}
/* 定义 arraymultiter_getsetlist 数组,包含 size 属性的 getter 函数 */
static PyGetSetDef arraymultiter_getsetlist[] = {
{"size",
(getter)arraymultiter_size_get,
NULL,
NULL, NULL},
{"index",
// 键名为 "index",对应的 getter 函数为 arraymultiter_index_get
(getter)arraymultiter_index_get,
// setter 为 NULL
NULL,
// docstring 为 NULL
NULL, NULL},
{"shape",
// 键名为 "shape",对应的 getter 函数为 arraymultiter_shape_get
(getter)arraymultiter_shape_get,
// setter 为 NULL
NULL,
// docstring 为 NULL
NULL, NULL},
{"iters",
// 键名为 "iters",对应的 getter 函数为 arraymultiter_iters_get
(getter)arraymultiter_iters_get,
// setter 为 NULL
NULL,
// docstring 为 NULL
NULL, NULL},
{NULL, NULL, NULL, NULL, NULL},
// 最后一行为 NULL 结束符
};
static PyMemberDef arraymultiter_members[] = {
{"numiter",
T_INT,
offsetof(PyArrayMultiIterObject, numiter),
READONLY, NULL},
{"nd",
T_INT,
offsetof(PyArrayMultiIterObject, nd),
READONLY, NULL},
{"ndim",
T_INT,
offsetof(PyArrayMultiIterObject, nd),
READONLY, NULL},
{NULL, 0, 0, 0, NULL},
};
static PyObject *
arraymultiter_reset(PyArrayMultiIterObject *self, PyObject *args)
{
// 解析参数元组,如果解析失败则返回 NULL
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
// 调用 NumPy C API 函数 PyArray_MultiIter_RESET 重置多重迭代器对象
PyArray_MultiIter_RESET(self);
// 返回 Python 中的 None 对象
Py_RETURN_NONE;
}
static PyMethodDef arraymultiter_methods[] = {
// reset 方法的定义
{"reset",
(PyCFunction) arraymultiter_reset,
METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}, /* sentinel */
};
NPY_NO_EXPORT PyTypeObject PyArrayMultiIter_Type = {
// PyArrayMultiIter_Type 对象的类型定义
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "numpy.broadcast", // 对象类型名称
.tp_basicsize = sizeof(PyArrayMultiIterObject), // 基本大小
.tp_dealloc = (destructor)arraymultiter_dealloc, // 析构函数
.tp_flags = Py_TPFLAGS_DEFAULT, // 默认标志
.tp_iternext = (iternextfunc)arraymultiter_next, // 迭代下一个函数
.tp_methods = arraymultiter_methods, // 方法列表
.tp_members = arraymultiter_members, // 成员列表
.tp_getset = arraymultiter_getsetlist, // 获取和设置方法列表
.tp_new = arraymultiter_new, // 新建对象的方法
};
/*========================= Neighborhood iterator ======================*/
static void neighiter_dealloc(PyArrayNeighborhoodIterObject* iter);
static char* _set_constant(PyArrayNeighborhoodIterObject* iter,
PyArrayObject *fill)
{
char *ret;
PyArrayIterObject *ar = iter->_internal_iter;
int storeflags, st;
// 分配内存给 ret,大小为数组元素的大小
ret = PyDataMem_NEW(PyArray_ITEMSIZE(ar->ao));
if (ret == NULL) {
// 分配内存失败,设置内存错误并返回 NULL
PyErr_SetNone(PyExc_MemoryError);
return NULL;
}
if (PyArray_ISOBJECT(ar->ao)) {
// 如果数组元素是对象类型,则复制填充数组中的 PyObject* 数据到 ret
memcpy(ret, PyArray_DATA(fill), sizeof(PyObject*));
// 增加引用计数
Py_INCREF(*(PyObject**)ret);
} else {
/* 非对象类型 */
// 存储原始标志
storeflags = PyArray_FLAGS(ar->ao);
// 设置数组行为标志
PyArray_ENABLEFLAGS(ar->ao, NPY_ARRAY_BEHAVED);
// 设置数组元素为 fill
st = PyArray_SETITEM(ar->ao, ret, (PyObject*)fill);
// 恢复原始标志
((PyArrayObject_fields *)ar->ao)->flags = storeflags;
if (st < 0) {
// 设置数组元素失败,释放内存并返回 NULL
PyDataMem_FREE(ret);
return NULL;
}
}
return ret;
}
bd = coordinates[c] + p->coordinates[c]; \
if (bd < p->limits[c][0] || bd > p->limits[c][1]) { \
return niter->constant; \
} \
_coordinates[c] = bd;
/* 根据当前坐标设置数据指针 */
static char*
get_ptr_constant(PyArrayIterObject* _iter, const npy_intp *coordinates)
{
int i;
npy_intp bd, _coordinates[NPY_MAXDIMS];
PyArrayNeighborhoodIterObject *niter = (PyArrayNeighborhoodIterObject*)_iter;
PyArrayIterObject *p = niter->_internal_iter;
for(i = 0; i < niter->nd; ++i) {
_INF_SET_PTR(i)
}
// 调用 translate 方法返回数据指针
return p->translate(p, _coordinates);
}
/*
* For an array x of dimension n, and given index i, returns j, 0 <= j < n
* such as x[i] = x[j], with x assumed to be mirrored. For example, for x =
* {1, 2, 3} (n = 3)
*
* index -5 -4 -3 -2 -1 0 1 2 3 4 5 6
* value 2 3 3 2 1 1 2 3 3 2 1 1
*
* __npy_pos_remainder(4, 3) will return 1, because x[4] = x[1]
*/
static inline npy_intp
__npy_pos_remainder(npy_intp i, npy_intp n)
{
npy_intp k, l, j;
/* Mirror i such that it is guaranteed to be positive */
if (i < 0) {
i = - i - 1;
}
/* Compute k and l such that i = k * n + l, 0 <= l < k */
k = i / n;
l = i - k * n;
if (_NPY_IS_EVEN(k)) {
j = l;
} else {
j = n - 1 - l;
}
return j;
}
/*
* Macro to set _coordinates[c] using mirrored indexing based on bounds
* and current coordinates for neighborhood iteration.
*/
lb = p->limits[c][0]; \
bd = coordinates[c] + p->coordinates[c] - lb; \
_coordinates[c] = lb + __npy_pos_remainder(bd, p->limits_sizes[c]);
/*
* Sets the data pointer (_coordinates) based on mirrored indexing for
* neighborhood iteration.
*/
static char*
get_ptr_mirror(PyArrayIterObject* _iter, const npy_intp *coordinates)
{
int i;
npy_intp bd, _coordinates[NPY_MAXDIMS], lb;
PyArrayNeighborhoodIterObject *niter = (PyArrayNeighborhoodIterObject*)_iter;
PyArrayIterObject *p = niter->_internal_iter;
for(i = 0; i < niter->nd; ++i) {
_INF_SET_PTR_MIRROR(i)
}
return p->translate(p, _coordinates);
}
/*
* Compute l such that i = k * n + l, 0 <= l < |k|
*/
static inline npy_intp
__npy_euclidean_division(npy_intp i, npy_intp n)
{
npy_intp l;
l = i % n;
if (l < 0) {
l += n;
}
return l;
}
/*
* Macro to set _coordinates[c] using circular indexing based on bounds
* and current coordinates for neighborhood iteration.
*/
lb = p->limits[c][0]; \
bd = coordinates[c] + p->coordinates[c] - lb; \
_coordinates[c] = lb + __npy_euclidean_division(bd, p->limits_sizes[c]);
/*
* Sets the data pointer (_coordinates) based on circular indexing for
* neighborhood iteration.
*/
static char*
get_ptr_circular(PyArrayIterObject* _iter, const npy_intp *coordinates)
{
int i;
npy_intp bd, _coordinates[NPY_MAXDIMS], lb;
PyArrayNeighborhoodIterObject *niter = (PyArrayNeighborhoodIterObject*)_iter;
PyArrayIterObject *p = niter->_internal_iter;
for(i = 0; i < niter->nd; ++i) {
_INF_SET_PTR_CIRCULAR(i)
}
return p->translate(p, _coordinates);
}
/*
* Create a new neighborhood iterator object.
*/
/*NUMPY_API
* A Neighborhood Iterator object.
*/
NPY_NO_EXPORT PyObject*
PyArray_NeighborhoodIterNew(PyArrayIterObject *x, const npy_intp *bounds,
int mode, PyArrayObject* fill)
{
int i;
PyArrayNeighborhoodIterObject *ret;
ret = PyArray_malloc(sizeof(*ret));
if (ret == NULL) {
return NULL;
}
PyObject_Init((PyObject *)ret, &PyArrayNeighborhoodIter_Type);
Py_INCREF(x->ao); /* PyArray_RawIterBaseInit steals a reference */
PyArray_RawIterBaseInit((PyArrayIterObject*)ret, x->ao);
Py_INCREF(x);
ret->_internal_iter = x;
ret->nd = PyArray_NDIM(x->ao);
/* Additional initialization and setup code continues beyond this point */
for (i = 0; i < ret->nd; ++i) {
ret->dimensions[i] = PyArray_DIMS(x->ao)[i];
}
/* 设置返回结构体中的维度信息为输入数组的维度信息 */
ret->dimensions[i] = PyArray_DIMS(x->ao)[i];
/* 计算邻域的大小并复制形状 */
ret->size = 1;
for (i = 0; i < ret->nd; ++i) {
ret->bounds[i][0] = bounds[2 * i];
ret->bounds[i][1] = bounds[2 * i + 1];
ret->size *= (ret->bounds[i][1] - ret->bounds[i][0]) + 1;
/* limits 用于跟踪邻域的有效范围:如果邻域的边界超出数组范围,则 limits 等于 boundaries。
* 相反,如果边界严格在数组内部,则 limits 对应于数组范围。例如,对于数组 [1, 2, 3],
* 如果边界是 [-1, 3],则 limits 是 [-1, 3];但如果边界是 [1, 2],则 limits 是 [0, 2]。
*
* 这在叠加在此迭代器之上的邻域迭代器中使用 */
ret->limits[i][0] = ret->bounds[i][0] < 0 ? ret->bounds[i][0] : 0;
ret->limits[i][1] = ret->bounds[i][1] >= ret->dimensions[i] - 1 ?
ret->bounds[i][1] :
ret->dimensions[i] - 1;
ret->limits_sizes[i] = (ret->limits[i][1] - ret->limits[i][0]) + 1;
}
/* 计算邻域的大小并复制形状 */
ret->size = 1;
for (i = 0; i < ret->nd; ++i) {
/* 设置邻域边界 */
ret->bounds[i][0] = bounds[2 * i];
ret->bounds[i][1] = bounds[2 * i + 1];
/* 计算邻域大小 */
ret->size *= (ret->bounds[i][1] - ret->bounds[i][0]) + 1;
/* limits 用于跟踪邻域的有效范围 */
ret->limits[i][0] = ret->bounds[i][0] < 0 ? ret->bounds[i][0] : 0;
ret->limits[i][1] = ret->bounds[i][1] >= ret->dimensions[i] - 1 ?
ret->bounds[i][1] :
ret->dimensions[i] - 1;
ret->limits_sizes[i] = (ret->limits[i][1] - ret->limits[i][0]) + 1;
}
switch (mode) {
case NPY_NEIGHBORHOOD_ITER_ZERO_PADDING:
ret->constant = PyArray_Zero(x->ao);
ret->mode = mode;
ret->translate = &get_ptr_constant;
break;
case NPY_NEIGHBORHOOD_ITER_ONE_PADDING:
ret->constant = PyArray_One(x->ao);
ret->mode = mode;
ret->translate = &get_ptr_constant;
break;
case NPY_NEIGHBORHOOD_ITER_CONSTANT_PADDING:
/* _set_constant 返回值中的新引用,如果数组对象 */
assert(PyArray_EquivArrTypes(x->ao, fill) == NPY_TRUE);
ret->constant = _set_constant(ret, fill);
if (ret->constant == NULL) {
goto clean_x;
}
ret->mode = mode;
ret->translate = &get_ptr_constant;
break;
case NPY_NEIGHBORHOOD_ITER_MIRROR_PADDING:
ret->mode = mode;
ret->constant = NULL;
ret->translate = &get_ptr_mirror;
break;
case NPY_NEIGHBORHOOD_ITER_CIRCULAR_PADDING:
ret->mode = mode;
ret->constant = NULL;
ret->translate = &get_ptr_circular;
break;
default:
PyErr_SetString(PyExc_ValueError, "Unsupported padding mode");
goto clean_x;
}
/* 根据不同的填充模式进行设置 */
switch (mode) {
case NPY_NEIGHBORHOOD_ITER_ZERO_PADDING:
/* 使用零填充模式 */
ret->constant = PyArray_Zero(x->ao);
ret->mode = mode;
ret->translate = &get_ptr_constant;
break;
case NPY_NEIGHBORHOOD_ITER_ONE_PADDING:
/* 使用一填充模式 */
ret->constant = PyArray_One(x->ao);
ret->mode = mode;
ret->translate = &get_ptr_constant;
break;
case NPY_NEIGHBORHOOD_ITER_CONSTANT_PADDING:
/* 常数填充模式,_set_constant 返回值中的新引用 */
assert(PyArray_EquivArrTypes(x->ao, fill) == NPY_TRUE);
ret->constant = _set_constant(ret, fill);
if (ret->constant == NULL) {
goto clean_x;
}
ret->mode = mode;
ret->translate = &get_ptr_constant;
break;
case NPY_NEIGHBORHOOD_ITER_MIRROR_PADDING:
/* 镜像填充模式 */
ret->mode = mode;
ret->constant = NULL;
ret->translate = &get_ptr_mirror;
break;
case NPY_NEIGHBORHOOD_ITER_CIRCULAR_PADDING:
/* 循环填充模式 */
ret->mode = mode;
ret->constant = NULL;
ret->translate = &get_ptr_circular;
break;
default:
/* 不支持的填充模式 */
PyErr_SetString(PyExc_ValueError, "Unsupported padding mode");
goto clean_x;
}
/*
* XXX: we force x iterator to be non contiguous because we need
* coordinates... Modifying the iterator here is not great
*/
x->contiguous = 0;
/* 强制 x 迭代器为非连续,因为我们需要坐标... 在这里修改迭代器并不是最佳做法 */
x->contiguous = 0;
PyArrayNeighborhoodIter_Reset(ret);
return (PyObject*)ret;
/* 重置邻域迭代器并返回其 PyObject* 类型的指针 */
PyArrayNeighborhoodIter_Reset(ret);
return (PyObject*)ret;
// 释放 clean_x 函数中 ret 结构体的内部迭代器资源
Py_DECREF(ret->_internal_iter);
// 调用 array_iter_base_dealloc 函数释放 ret 结构体的基类迭代器资源
array_iter_base_dealloc((PyArrayIterObject*)ret);
// 释放 ret 结构体本身的内存资源
PyArray_free((PyArrayObject*)ret);
// 返回空指针表示操作失败
return NULL;
}
// 释放 PyArrayNeighborhoodIterObject 结构体的资源
static void neighiter_dealloc(PyArrayNeighborhoodIterObject* iter)
{
// 如果迭代器模式是常量填充模式
if (iter->mode == NPY_NEIGHBORHOOD_ITER_CONSTANT_PADDING) {
// 如果数组对象是对象数组,则释放常量指针指向的对象
if (PyArray_ISOBJECT(iter->_internal_iter->ao)) {
Py_DECREF(*(PyObject**)iter->constant);
}
}
// 释放常量数组的内存资源
PyDataMem_FREE(iter->constant);
// 释放内部迭代器资源
Py_DECREF(iter->_internal_iter);
// 调用 array_iter_base_dealloc 函数释放 iter 结构体的基类迭代器资源
array_iter_base_dealloc((PyArrayIterObject*)iter);
// 释放 iter 结构体本身的内存资源
PyArray_free((PyArrayObject*)iter);
}
// 定义 PyArrayNeighborhoodIter_Type 类型的静态属性
NPY_NO_EXPORT PyTypeObject PyArrayNeighborhoodIter_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
// 类型名称为 "numpy.neigh_internal_iter"
.tp_name = "numpy.neigh_internal_iter",
// 类型对象的基本大小为 PyArrayNeighborhoodIterObject 结构体大小
.tp_basicsize = sizeof(PyArrayNeighborhoodIterObject),
// 设置析构函数为 neighiter_dealloc 函数
.tp_dealloc = (destructor)neighiter_dealloc,
// 设置默认标志位
.tp_flags = Py_TPFLAGS_DEFAULT,
};
.\numpy\numpy\_core\src\multiarray\iterators.h
NPY_NO_EXPORT PyObject
*iter_subscript(PyArrayIterObject *, PyObject *);
NPY_NO_EXPORT int
iter_ass_subscript(PyArrayIterObject *, PyObject *, PyObject *);
NPY_NO_EXPORT void
PyArray_RawIterBaseInit(PyArrayIterObject *it, PyArrayObject *ao);
.\numpy\numpy\_core\src\multiarray\legacy_dtype_implementation.c
/*
* 此部分代码定义了一些用于比较和判断旧版数据类型的函数和宏。
* 这些函数和宏主要用于处理旧版数据类型,可能在将来被弃用和移除。
*/
/*
* 比较两个数据类型的字段字典是否相等。
* 如果两个描述符的字段类型和字段名称相等且顺序相同,则返回1,否则返回0。
*/
static int
_equivalent_fields(_PyArray_LegacyDescr *type1, _PyArray_LegacyDescr *type2) {
int val;
if (type1->fields == type2->fields && type1->names == type2->names) {
return 1;
}
if (type1->fields == NULL || type2->fields == NULL) {
return 0;
}
val = PyObject_RichCompareBool(type1->fields, type2->fields, Py_EQ);
if (val != 1 || PyErr_Occurred()) {
PyErr_Clear();
return 0;
}
val = PyObject_RichCompareBool(type1->names, type2->names, Py_EQ);
if (val != 1 || PyErr_Occurred()) {
PyErr_Clear();
return 0;
}
return 1;
}
/*
* 比较两个数据类型的子数组数据是否相等。
* 如果相等则返回1,否则返回0。
*/
static int
_equivalent_subarrays(PyArray_ArrayDescr *sub1, PyArray_ArrayDescr *sub2)
{
int val;
if (sub1 == sub2) {
return 1;
}
if (sub1 == NULL || sub2 == NULL) {
return 0;
}
val = PyObject_RichCompareBool(sub1->shape, sub2->shape, Py_EQ);
if (val != 1 || PyErr_Occurred()) {
PyErr_Clear();
return 0;
}
return PyArray_EquivTypes(sub1->base, sub2->base);
}
/*
* 判断两个数据描述符是否等价。
* 如果是旧版数据类型且各种条件满足则返回NPY_TRUE,否则返回NPY_FALSE。
*/
static unsigned char
PyArray_LegacyEquivTypes(PyArray_Descr *type1, PyArray_Descr *type2)
{
int type_num1, type_num2, size1, size2;
if (type1 == type2) {
return NPY_TRUE;
}
if (!PyDataType_ISLEGACY(type1) || !PyDataType_ISLEGACY(type2)) {
return NPY_FALSE;
}
type_num1 = type1->type_num;
type_num2 = type2->type_num;
size1 = type1->elsize;
size2 = type2->elsize;
if (size1 != size2) {
return NPY_FALSE;
}
if (PyArray_ISNBO(type1->byteorder) != PyArray_ISNBO(type2->byteorder)) {
return NPY_FALSE;
}
if (PyDataType_SUBARRAY(type1) || PyDataType_SUBARRAY(type2)) {
return ((type_num1 == type_num2)
&& _equivalent_subarrays(PyDataType_SUBARRAY(type1), PyDataType_SUBARRAY(type2)));
}
if (type_num1 == NPY_VOID || type_num2 == NPY_VOID) {
return ((type_num1 == type_num2) && _equivalent_fields(
(_PyArray_LegacyDescr *)type1, (_PyArray_LegacyDescr *)type2));
}
if (type_num1 == NPY_DATETIME
|| type_num1 == NPY_TIMEDELTA
|| type_num2 == NPY_DATETIME
|| type_num2 == NPY_TIMEDELTA) {
return ((type_num1 == type_num2)
&& has_equivalent_datetime_metadata(type1, type2));
}
return type1->kind == type2->kind;
/*
* 比较两个类型描述符的等价性。
*
* 如果 typenum1 和 typenum2 相等,则返回 NPY_SUCCEED;否则返回 NPY_FAIL。
*/
static unsigned char
PyArray_LegacyEquivTypenums(int typenum1, int typenum2)
{
PyArray_Descr *d1, *d2;
npy_bool ret;
// 检查 typenum1 和 typenum2 是否相等
if (typenum1 == typenum2) {
return NPY_SUCCEED;
}
// 获取 typenum1 和 typenum2 对应的类型描述符
d1 = PyArray_DescrFromType(typenum1);
d2 = PyArray_DescrFromType(typenum2);
// 判断两个类型描述符是否等价
ret = PyArray_LegacyEquivTypes(d1, d2);
// 释放类型描述符的引用
Py_DECREF(d1);
Py_DECREF(d2);
return ret;
}
/*
* 检查是否可以安全地从一个类型转换到另一个类型。
*
* 如果 fromtype 和 totype 相等,则返回 1;否则根据类型转换表格判断是否可以安全转换。
* 如果可以安全转换,则返回 1;否则返回 0。
*/
static int
PyArray_LegacyCanCastSafely(int fromtype, int totype)
{
PyArray_Descr *from;
/* 快速查找小类型编号的表格 */
if ((unsigned int)fromtype < NPY_NTYPES_LEGACY &&
(unsigned int)totype < NPY_NTYPES_LEGACY) {
return _npy_can_cast_safely_table[fromtype][totype];
}
/* 判断是否同一类型 */
if (fromtype == totype) {
return 1;
}
// 获取 fromtype 对应的类型描述符
from = PyArray_DescrFromType(fromtype);
/*
* cancastto 是一个以 NPY_NOTYPE 结尾的 C 整型数组,表示该数据类型可以安全转换的目标类型。
* 遍历数组,查找是否存在可以安全转换到 totype 的目标类型。
*/
if (PyDataType_GetArrFuncs(from)->cancastto) {
int *curtype = PyDataType_GetArrFuncs(from)->cancastto;
while (*curtype != NPY_NOTYPE) {
if (*curtype++ == totype) {
Py_DECREF(from);
return 1;
}
}
}
// 释放类型描述符的引用
Py_DECREF(from);
return 0;
}
/*
* 检查是否可以安全地将一个类型描述符转换为另一个类型描述符。
*
* 根据 from 和 to 的类型描述符,判断是否可以安全转换。
* 如果可以安全转换,则返回 1;否则返回 0。
*/
static npy_bool
PyArray_LegacyCanCastTo(PyArray_Descr *from, PyArray_Descr *to)
{
int from_type_num = from->type_num;
int to_type_num = to->type_num;
npy_bool ret;
// 调用 PyArray_LegacyCanCastSafely 函数判断是否可以安全转换
ret = (npy_bool) PyArray_LegacyCanCastSafely(from_type_num, to_type_num);
return ret;
}
/*
* 比较两个字段字典是否可以安全地进行类型转换。
*
* 如果 field1 和 field2 相同,则返回 1;
* 如果 field1 或 field2 为空,则返回 0;
* 如果 field1 和 field2 中的字段数量不同,则返回 0;
* 遍历 field1 和 field2 中的每个字段,并比较其类型是否可以安全转换。
* 如果所有字段都可以安全转换,则返回 1;否则返回 0。
*/
static int
can_cast_fields(PyObject *field1, PyObject *field2, NPY_CASTING casting)
{
Py_ssize_t ppos;
PyObject *key;
PyObject *tuple1, *tuple2;
// 检查 field1 和 field2 是否是同一个对象
if (field1 == field2) {
return 1;
}
// 检查 field1 和 field2 是否为空
if (field1 == NULL || field2 == NULL) {
return 0;
}
// 检查 field1 和 field2 的字段数量是否相同
if (PyDict_Size(field1) != PyDict_Size(field2)) {
return 0;
}
/* 迭代比较所有字段并检查是否可以安全转换 */
ppos = 0;
while (PyDict_Next(field1, &ppos, &key, &tuple1)) {
// 获取 field2 中与 key 对应的值
if ((tuple2 = PyDict_GetItem(field2, key)) == NULL) {
return 0;
}
/* 比较字段的 dtype 是否可以安全转换 */
if (!PyArray_CanCastTypeTo(
(PyArray_Descr *)PyTuple_GET_ITEM(tuple1, 0),
(PyArray_Descr *)PyTuple_GET_ITEM(tuple2, 0),
casting)) {
return 0;
}
}
return 1;
}
/*
* 检查是否可以安全地将一个类型描述符转换为另一个类型描述符。
*
* 根据 from 和 to 的类型描述符,判断是否可以安全转换。
* 如果可以安全转换,则返回 NPY_SUCCEED;否则返回 NPY_FAIL。
*/
NPY_NO_EXPORT npy_bool
PyArray_LegacyCanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to,
NPY_CASTING casting)
{
// 将 from 和 to 强制转换为 _PyArray_LegacyDescr 结构体指针
_PyArray_LegacyDescr *lfrom = (_PyArray_LegacyDescr *)from;
_PyArray_LegacyDescr *lto = (_PyArray_LegacyDescr *)to;
/*
* 快速路径:处理相等性和基本类型的情况。
*/
if (from == to ||
((NPY_LIKELY(PyDataType_ISNUMBER(from)) ||
PyDataType_ISOBJECT(from)) &&
NPY_LIKELY(from->type_num == to->type_num) &&
NPY_LIKELY(from->byteorder == to->byteorder))) {
return 1;
}
// 如果 from 或 to 不是遗留数据类型,则返回不可转换
if (!PyDataType_ISLEGACY(from) || !PyDataType_ISLEGACY(to)) {
return 0;
}
/*
* 处理包含子数组和字段的情况需要特殊处理。
*/
if (PyDataType_HASFIELDS(from)) {
/*
* 如果 from 是结构化数据类型,则只能在不安全转换的情况下将其转换为非对象类型,
* 前提是它只有一个字段;递归处理,以防单个字段本身是结构化的。
*/
if (!PyDataType_HASFIELDS(to) && !PyDataType_ISOBJECT(to)) {
if (casting == NPY_UNSAFE_CASTING &&
PyDict_Size(lfrom->fields) == 1) {
Py_ssize_t ppos = 0;
PyObject *tuple;
PyArray_Descr *field;
PyDict_Next(lfrom->fields, &ppos, NULL, &tuple);
field = (PyArray_Descr *)PyTuple_GET_ITEM(tuple, 0);
/*
* 对于子数组,我们需要获取其基础类型;
* 由于我们已经在进行不安全转换,可以忽略其形状。
*/
if (PyDataType_HASSUBARRAY(field)) {
field = PyDataType_SUBARRAY(field)->base;
}
return PyArray_LegacyCanCastTypeTo(field, to, casting);
}
else {
return 0;
}
}
/*
* 从一个结构化数据类型到另一个的转换依赖于字段;
* 我们将此情况传递给下面的 EquivTypenums 情况处理。
*
* TODO: 将上面的部分移到这里?需要检查等价类型编号是否是必需的附加约束。
*
* TODO/FIXME: 目前,对于不安全转换,始终允许结构化到结构化的转换;
* 这是不正确的,但因为 can_cast 下面的处理与 astype 不同步,所以需要;参见 gh-13667。
*/
if (casting == NPY_UNSAFE_CASTING) {
return 1;
}
}
else if (PyDataType_HASFIELDS(to)) {
/*
* 如果 "from" 是简单数据类型而 "to" 具有字段,则仅不安全转换有效
* (即使对多个字段也是如此)。
*/
return casting == NPY_UNSAFE_CASTING;
}
/*
* 对于其他情况,我们暂时认为可以进行不安全转换。
* FIXME: 确保这里的操作与 "astype" 一致,
* 即更正确地处理子数组和用户定义的数据类型。
*/
else if (casting == NPY_UNSAFE_CASTING) {
// 如果 casting 等于 NPY_UNSAFE_CASTING,则返回 1,表示允许不安全的类型转换
return 1;
}
/*
* Equivalent simple types can be cast with any value of 'casting', but
* we need to be careful about structured to structured.
*/
}
// 如果允许安全或者同种类的类型转换
else if (casting == NPY_SAFE_CASTING || casting == NPY_SAME_KIND_CASTING) {
// 检查是否可以使用旧的 PyArray_LegacyCanCastTo 函数进行转换
if (PyArray_LegacyCanCastTo(from, to)) {
return 1; // 可以转换则返回 1
}
else if(casting == NPY_SAME_KIND_CASTING) {
/*
* Also allow casting from lower to higher kinds, according
* to the ordering provided by dtype_kind_to_ordering.
* Some kinds, like datetime, don't fit in the hierarchy,
* and are special cased as -1.
*/
int from_order, to_order;
// 获取源类型和目标类型的类型顺序
from_order = dtype_kind_to_ordering(from->kind);
to_order = dtype_kind_to_ordering(to->kind);
if (to->kind == 'm') {
// 对于 'm' 类型 (timedelta),直接返回是否源类型在整数类型之前
int integer_order = dtype_kind_to_ordering('i');
return (from_order != -1) && (from_order <= integer_order);
}
// 一般情况下返回是否源类型顺序在目标类型顺序之前
return (from_order != -1) && (from_order <= to_order);
}
else {
return 0; // 其他情况返回 0,表示不允许类型转换
}
}
// 如果 casting 设置为 NPY_NO_CASTING 或者 NPY_EQUIV_CASTING
else {
return 0; // 返回 0,表示不允许类型转换
}
}
# 这行代码结束了一个 Python 函数的定义,闭合了函数的代码块
.\numpy\numpy\_core\src\multiarray\legacy_dtype_implementation.h
// 定义了一个条件编译指令,用于避免重复包含同一头文件
// 如果 NUMPY_CORE_SRC_MULTIARRAY_LEGACY_DTYPE_IMPLEMENTATION_H_ 未定义,则编译以下内容
// 导出了一个名为 npy_bool 的非公开符号
// 该符号代表一个布尔类型,用于 NumPy 中的类型转换判断
NPY_NO_EXPORT npy_bool
// 函数声明:用于判断从一个数组描述符(from)是否可以转换为另一个数组描述符(to)
PyArray_LegacyCanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to,
NPY_CASTING casting);
// 结束条件编译指令,确保该头文件内容只被包含一次
.\numpy\numpy\_core\src\multiarray\mapping.c
/*
* Define macro to specify the NPY API version without deprecated features
*/
/*
* Define macros to specify modules for multiarray and umath
*/
/*
* Clean PY_SSIZE_T type usage to ensure compatibility
*/
/*
* Include Python core header files and structmember.h for member descriptors
*/
/*
* Include numpy array object and math utility headers
*/
/*
* Include necessary custom numpy headers
*/
/*
* Define code for iterator implementation for nditer until made public
*/
/*
* Include umathmodule header for universal functions in numpy
*/
/*
* Define constants to indicate presence of different index types in numpy arrays
*/
/*
* Implementation of array_length function to provide length of array
* Supports arrays with non-zero dimensions
* Throws TypeError for unsized objects (0-dimensional arrays)
*/
NPY_NO_EXPORT Py_ssize_t
array_length(PyArrayObject *self)
{
if (PyArray_NDIM(self) != 0) {
return PyArray_DIMS(self)[0]; // Return the length of the first dimension
} else {
PyErr_SetString(PyExc_TypeError, "len() of unsized object"); // Raise TypeError for unsized objects
return -1;
}
}
/*
* Helper function for PyArray_MapIterSwapAxes and related functions
* Generates a tuple for transpose based on specified dimensions
*/
static void
_get_transpose(int fancy_ndim, int consec, int ndim, int getmap, npy_intp *dims)
{
/*
* For getting the array the tuple for transpose is
* (n1,...,n1+n2-1,0,...,n1-1,n1+n2,...,n3-1)
* n1 is the number of dimensions of the broadcast index array
* n2 is the number of dimensions skipped at the start
* n3 is the number of dimensions of the result
*/
/*
* For setting the array the tuple for transpose is
* (n2,...,n1+n2-1,0,...,n2-1,n1+n2,...n3-1)
*/
int n1 = fancy_ndim;
int n2 = consec; // axes to insert at
int n3 = ndim;
// Determine the boundary value based on operation type (get or set)
int bnd = getmap ? n1 : n2;
int val = bnd;
int i = 0;
// Generate the tuple for transpose based on calculated dimensions
while (val < n1 + n2) {
dims[i++] = val++;
}
val = 0;
while (val < bnd) {
dims[i++] = val++;
}
val = n1 + n2;
while (val < n3) {
dims[i++] = val++;
}
}
/*
* Swap the axes to or from their inserted form. MapIter always puts the
* advanced (array) indices first in the iteration. But if they are
* consecutive, will insert/transpose them back before returning.
* This is stored as `mit->consec != 0` (the place where they are inserted)
* For assignments, the opposite happens: The values to be assigned are
* transposed (getmap=1 instead of getmap=0). `getmap=0` and `getmap=1`
* undo the other operation.
*/
NPY_NO_EXPORT void
PyArray_MapIterSwapAxes(PyArrayMapIterObject *mit, PyArrayObject **ret, int getmap)
{
PyObject *new;
PyArray_Dims permute;
npy_intp d[NPY_MAXDIMS];
PyArrayObject *arr;
permute.ptr = d;
permute.len = mit->nd;
/*
* arr might not have the right number of dimensions
* and need to be reshaped first by prepending ones
*/
arr = *ret;
if (PyArray_NDIM(arr) != mit->nd) {
// Calculate dimensions for reshaping arr
for (int i = 1; i <= PyArray_NDIM(arr); i++) {
permute.ptr[mit->nd-i] = PyArray_DIMS(arr)[PyArray_NDIM(arr)-i];
}
// Prepend ones if necessary
for (int i = 0; i < mit->nd-PyArray_NDIM(arr); i++) {
permute.ptr[i] = 1;
}
// Reshape arr with the new dimensions
new = PyArray_Newshape(arr, &permute, NPY_ANYORDER);
Py_DECREF(arr);
*ret = (PyArrayObject *)new;
if (new == NULL) {
return;
}
}
// Perform transpose operation based on getmap flag
_get_transpose(mit->nd_fancy, mit->consec, mit->nd, getmap, permute.ptr);
// Transpose *ret array using permute dimensions
new = PyArray_Transpose(*ret, &permute);
Py_DECREF(*ret);
*ret = (PyArrayObject *)new;
}
static inline void
multi_DECREF(PyObject **objects, npy_intp n)
{
npy_intp i;
for (i = 0; i < n; i++) {
Py_DECREF(objects[i]);
}
}
/**
* Unpack a tuple into an array of new references. Returns the number of objects
* unpacked.
*
* Useful if a tuple is being iterated over multiple times, or for a code path
* that doesn't always want the overhead of allocating a tuple.
*/
static inline npy_intp
unpack_tuple(PyTupleObject *index, PyObject **result, npy_intp result_n)
{
npy_intp n, i;
n = PyTuple_GET_SIZE(index);
if (n > result_n) {
PyErr_SetString(PyExc_IndexError,
"too many indices for array");
return -1;
}
// Unpack tuple into result array and increment references
for (i = 0; i < n; i++) {
result[i] = PyTuple_GET_ITEM(index, i);
Py_INCREF(result[i]);
}
return n;
}
/* Unpack a single scalar index, taking a new reference to match unpack_tuple */
static inline npy_intp
unpack_scalar(PyObject *index, PyObject **result, npy_intp NPY_UNUSED(result_n))
{
// Increment reference for the scalar index
Py_INCREF(index);
result[0] = index;
return 1;
}
/**
* Turn an index argument into a c-array of `PyObject *`s, one for each index.
*
* When a tuple is passed, the tuple elements are unpacked into the buffer.
* Anything else is handled by unpack_scalar().
*
* @param index The index object, which may or may not be a tuple. This is
* a borrowed reference.
* @param result An empty buffer of PyObject* to write each index component
* to. The references written are new.
* @param result_n The length of the result buffer
*
* @returns The number of items in `result`, or -1 if an error occurred.
* The entries in `result` at and beyond this index should be
* assumed to contain garbage, even if they were initialized
* to NULL, so are not safe to Py_XDECREF. Use multi_DECREF to
* dispose of them.
*/
NPY_NO_EXPORT npy_intp
unpack_indices(PyObject *index, PyObject **result, npy_intp result_n)
{
/* It is likely that the logic here can be simplified. See the discussion
* on https://github.com/numpy/numpy/pull/21029
*/
/* Fast route for passing a tuple */
if (PyTuple_CheckExact(index)) {
// 如果传入的是元组,则调用 unpack_tuple 函数解压元组到 result 中
return unpack_tuple((PyTupleObject *)index, result, result_n);
}
/*
* Passing a tuple subclass - coerce to the base type. This incurs an
* allocation, but doesn't need to be a fast path anyway. Note that by
* calling `PySequence_Tuple`, we ensure that the subclass `__iter__` is
* called.
*/
if (PyTuple_Check(index)) {
// 如果传入的是元组的子类,则强制转换为基础类型元组,并解压到 result 中
PyTupleObject *tup = (PyTupleObject *) PySequence_Tuple(index);
if (tup == NULL) {
return -1;
}
npy_intp n = unpack_tuple(tup, result, result_n);
Py_DECREF(tup);
return n;
}
// 对于其他情况,调用 unpack_scalar 处理单个索引
return unpack_scalar(index, result, result_n);
}
/**
* Prepare an npy_index_object from the python slicing object.
*
* This function handles all index preparations with the exception
* of field access. It fills the array of index_info structs correctly.
* It already handles the boolean array special case for fancy indexing,
* i.e. if the index type is boolean, it is exactly one matching boolean
* array. If the index type is fancy, the boolean array is already
* converted to integer arrays. There is (as before) no checking of the
* boolean dimension.
*
* Checks everything but the bounds.
*
* @param the array being indexed
* @param the index object
* @param index info struct being filled (size of NPY_MAXDIMS * 2 + 1)
* @param number of indices found
* @param dimension of the indexing result
* @param dimension of the fancy/advanced indices part
* @param whether to allow the boolean special case
*
* @returns the index_type or -1 on failure and fills the number of indices.
*/
NPY_NO_EXPORT int
/*
* 准备索引函数,根据给定的索引对象和数组信息,解析并准备索引信息
*/
prepare_index(PyArrayObject *self, PyObject *index,
npy_index_info *indices,
int *num, int *ndim, int *out_fancy_ndim, int allow_boolean)
{
int new_ndim, fancy_ndim, used_ndim, index_ndim;
int curr_idx, get_idx;
int i;
npy_intp n;
PyObject *obj = NULL;
PyArrayObject *arr;
int index_type = 0;
int ellipsis_pos = -1;
/*
* 选择解包 `2*NPY_MAXDIMS` 项的历史原因。
* 最长的“合理”索引,生成的结果维度最多为 32,
* 是 `(0,)*ncu.MAXDIMS + (None,)*ncu.MAXDIMS`。
* 更长的索引可能存在,但并不常见。
*/
PyObject *raw_indices[NPY_MAXDIMS*2];
// 解包索引对象,填充 raw_indices 数组,返回索引的维度
index_ndim = unpack_indices(index, raw_indices, NPY_MAXDIMS*2);
if (index_ndim == -1) {
return -1;
}
/*
* 将所有索引解析到 indices 数组中的 index_info 结构体中
*/
used_ndim = 0;
new_ndim = 0;
fancy_ndim = 0;
get_idx = 0;
curr_idx = 0;
/*
* 比较索引的维度和实际的维度。这是为了找到省略号值或在必要时添加省略号。
*/
if (used_ndim < PyArray_NDIM(self)) {
if (index_type & HAS_ELLIPSIS) {
// 设置省略号的值并更新维度计数
indices[ellipsis_pos].value = PyArray_NDIM(self) - used_ndim;
used_ndim = PyArray_NDIM(self);
new_ndim += indices[ellipsis_pos].value;
}
else {
/*
* 尚未有省略号,但索引不完整,因此在末尾添加省略号。
*/
index_type |= HAS_ELLIPSIS;
indices[curr_idx].object = NULL;
indices[curr_idx].type = HAS_ELLIPSIS;
indices[curr_idx].value = PyArray_NDIM(self) - used_ndim;
ellipsis_pos = curr_idx;
used_ndim = PyArray_NDIM(self);
new_ndim += indices[curr_idx].value;
curr_idx += 1;
}
}
else if (used_ndim > PyArray_NDIM(self)) {
// 索引的维度超过数组的维度,抛出 IndexError 异常
PyErr_Format(PyExc_IndexError,
"too many indices for array: "
"array is %d-dimensional, but %d were indexed",
PyArray_NDIM(self),
used_ndim);
goto failed_building_indices;
}
else if (index_ndim == 0) {
/*
* 0 维度索引到 0 维度数组,即 array[()]。
* 我们将其视为整数索引,返回标量值。
* 这是有道理的,因为 array[...] 返回数组,而 array[()] 返回标量。
*/
used_ndim = 0;
index_type = HAS_INTEGER;
}
/* HAS_SCALAR_ARRAY 需要清理 index_type */
if (index_type & HAS_SCALAR_ARRAY) {
/* 如果索引类型包含标志 HAS_SCALAR_ARRAY */
/* 清除这个信息,因为后续处理中不需要它,会增加复杂度 */
if (index_type & HAS_FANCY) {
index_type -= HAS_SCALAR_ARRAY;
}
/* 对于完整的整数索引,标量数组被视为整数索引的一部分 */
else if (index_type == (HAS_INTEGER | HAS_SCALAR_ARRAY)) {
index_type -= HAS_SCALAR_ARRAY;
}
}
/*
* 到这一步,索引已经全部正确设置,没有进行边界检查,
* 新的数组可能仍然具有比可能的维度更多的维度,
* 并且布尔索引数组的形状可能不正确。
*
* 现在检查这些,这样我们以后就不必担心了。
* 这可能发生在使用 fancy indexing 或者 newaxis 时。
* 这意味着在维度过多时,广播错误的情况会更少发生。
*/
if (index_type & (HAS_NEWAXIS | HAS_FANCY)) {
if (new_ndim + fancy_ndim > NPY_MAXDIMS) {
/* 如果新的数组维度和 fancy 索引维度总和超过了 NPY_MAXDIMS */
/* 报错,指数的数量必须在 [0, NPY_MAXDIMS] 范围内,索引结果会有 %d 维度 */
PyErr_Format(PyExc_IndexError,
"number of dimensions must be within [0, %d], "
"indexing result would have %d",
NPY_MAXDIMS, (new_ndim + fancy_ndim));
goto failed_building_indices;
}
/*
* 如果我们有一个 fancy 索引,可能有一个布尔数组索引。
* 现在检查它的形状是否正确,因为我们可以找出它作用的轴。
*/
used_ndim = 0;
for (i = 0; i < curr_idx; i++) {
if ((indices[i].type == HAS_FANCY) && indices[i].value > 0) {
if (indices[i].value != PyArray_DIM(self, used_ndim)) {
char err_msg[174];
PyOS_snprintf(err_msg, sizeof(err_msg),
"boolean index did not match indexed array along "
"axis %d; size of axis is %" NPY_INTP_FMT
" but size of corresponding boolean axis is %" NPY_INTP_FMT,
used_ndim, PyArray_DIM(self, used_ndim),
indices[i].value);
PyErr_SetString(PyExc_IndexError, err_msg);
goto failed_building_indices;
}
}
if (indices[i].type == HAS_ELLIPSIS) {
used_ndim += indices[i].value;
}
else if ((indices[i].type == HAS_NEWAXIS) ||
(indices[i].type == HAS_0D_BOOL)) {
used_ndim += 0;
}
else {
used_ndim += 1;
}
}
}
/* 将 curr_idx 赋值给 num */
*num = curr_idx;
/* 将 new_ndim + fancy_ndim 赋值给 ndim */
*ndim = new_ndim + fancy_ndim;
/* 将 fancy_ndim 赋值给 out_fancy_ndim */
*out_fancy_ndim = fancy_ndim;
/* 减少 raw_indices 的引用计数,释放内存 */
multi_DECREF(raw_indices, index_ndim);
/* 返回索引类型 */
return index_type;
failed_building_indices:
/* 处理构建索引失败的情况 */
for (i=0; i < curr_idx; i++) {
Py_XDECREF(indices[i].object);
}
/* 减少 raw_indices 的引用计数,释放内存 */
multi_DECREF(raw_indices, index_ndim);
/* 返回错误状态 */
return -1;
/**
* Check if self has memory overlap with one of the index arrays, or with extra_op.
*
* @returns 1 if memory overlap found, 0 if not.
*/
NPY_NO_EXPORT int
index_has_memory_overlap(PyArrayObject *self,
int index_type, npy_index_info *indices, int num,
PyObject *extra_op)
{
int i;
// 如果索引类型包含花式索引或布尔索引
if (index_type & (HAS_FANCY | HAS_BOOL)) {
// 遍历索引数组
for (i = 0; i < num; ++i) {
// 如果索引对象存在且为数组,并且与 self 存在内存重叠
if (indices[i].object != NULL &&
PyArray_Check(indices[i].object) &&
solve_may_share_memory(self,
(PyArrayObject *)indices[i].object,
1) != 0) {
return 1;
}
}
}
// 如果存在额外操作对象,并且额外操作对象是数组,并且与 self 存在内存重叠
if (extra_op != NULL && PyArray_Check(extra_op) &&
solve_may_share_memory(self, (PyArrayObject *)extra_op, 1) != 0) {
return 1;
}
// 没有发现内存重叠
return 0;
}
/**
* Get pointer for an integer index.
*
* For a purely integer index, set ptr to the memory address.
* Returns 0 on success, -1 on failure.
* The caller must ensure that the index is a full integer
* one.
*
* @param Array being indexed
* @param result pointer
* @param parsed index information
* @param number of indices
*
* @return 0 on success -1 on failure
*/
static int
get_item_pointer(PyArrayObject *self, char **ptr,
npy_index_info *indices, int index_num) {
int i;
// 设置指针为数组的起始地址
*ptr = PyArray_BYTES(self);
// 遍历所有索引
for (i=0; i < index_num; i++) {
// 检查并调整索引值,确保在有效范围内
if ((check_and_adjust_index(&(indices[i].value),
PyArray_DIMS(self)[i], i, NULL)) < 0) {
return -1;
}
// 计算指针偏移量
*ptr += PyArray_STRIDE(self, i) * indices[i].value;
}
return 0;
}
/**
* Get view into an array using all non-array indices.
*
* For any index, get a view of the subspace into the original
* array. If there are no fancy indices, this is the result of
* the indexing operation.
* Ensure_array allows to fetch a safe subspace view for advanced
* indexing.
*
* @param Array being indexed
* @param resulting array (new reference)
* @param parsed index information
* @param number of indices
* @param Whether result should inherit the type from self
*
* @return 0 on success -1 on failure
*/
static int
get_view_from_index(PyArrayObject *self, PyArrayObject **view,
npy_index_info *indices, int index_num, int ensure_array) {
npy_intp new_strides[NPY_MAXDIMS];
npy_intp new_shape[NPY_MAXDIMS];
int i, j;
int new_dim = 0;
int orig_dim = 0;
// 获取数组的起始地址
char *data_ptr = PyArray_BYTES(self);
/* for slice parsing */
npy_intp start, stop, step, n_steps;
// 更多功能索引解析...
for (i=0; i < index_num; i++) {
// 根据索引类型进行处理
switch (indices[i].type) {
case HAS_INTEGER:
// 如果索引类型是整数,检查并调整索引值
if ((check_and_adjust_index(&indices[i].value,
PyArray_DIMS(self)[orig_dim], orig_dim,
NULL)) < 0) {
return -1;
}
// 根据数组在原始维度上的步长和调整后的索引值更新数据指针位置
data_ptr += PyArray_STRIDE(self, orig_dim) * indices[i].value;
// 增加新维度的计数,原始维度加一
new_dim += 0;
orig_dim += 1;
break;
case HAS_ELLIPSIS:
// 如果索引类型是省略号,根据省略号值设置新的步长和形状
for (j=0; j < indices[i].value; j++) {
new_strides[new_dim] = PyArray_STRIDE(self, orig_dim);
new_shape[new_dim] = PyArray_DIMS(self)[orig_dim];
new_dim += 1;
orig_dim += 1;
}
break;
case HAS_SLICE:
// 如果索引类型是切片,获取切片的起始、停止、步长等参数
if (PySlice_GetIndicesEx(indices[i].object,
PyArray_DIMS(self)[orig_dim],
&start, &stop, &step, &n_steps) < 0) {
return -1;
}
// 处理步长非正数的情况
if (n_steps <= 0) {
/* TODO: Always points to start then, could change that */
n_steps = 0;
step = 1;
start = 0;
}
// 根据切片的起始位置更新数据指针位置
data_ptr += PyArray_STRIDE(self, orig_dim) * start;
// 设置新维度的步长和形状
new_strides[new_dim] = PyArray_STRIDE(self, orig_dim) * step;
new_shape[new_dim] = n_steps;
new_dim += 1;
orig_dim += 1;
break;
case HAS_NEWAXIS:
// 如果索引类型是新轴,设置新轴的步长为0,形状为1
new_strides[new_dim] = 0;
new_shape[new_dim] = 1;
new_dim += 1;
break;
/* Fancy and 0-d boolean indices are ignored here */
case HAS_0D_BOOL:
// 忽略特殊情况:精确索引和0维布尔索引
break;
default:
// 默认情况下,增加新维度计数,原始维度加一
new_dim += 0;
orig_dim += 1;
break;
}
}
/* 创建新视图并设置基础数组 */
// 增加基础数组的引用计数
Py_INCREF(PyArray_DESCR(self));
// 使用给定的描述符和参数创建新的数组视图
*view = (PyArrayObject *)PyArray_NewFromDescr_int(
ensure_array ? &PyArray_Type : Py_TYPE(self),
PyArray_DESCR(self),
new_dim, new_shape, new_strides, data_ptr,
PyArray_FLAGS(self),
ensure_array ? NULL : (PyObject *)self,
(PyObject *)self, _NPY_ARRAY_ENSURE_DTYPE_IDENTITY);
// 检查视图创建是否成功
if (*view == NULL) {
return -1;
}
// 返回成功
return 0;
}
/*
* Implements boolean indexing. This produces a one-dimensional
* array which picks out all of the elements of 'self' for which
* the corresponding element of 'op' is True.
*
* This operation is somewhat unfortunate, because to produce
* a one-dimensional output array, it has to choose a particular
* iteration order, in the case of NumPy that is always C order even
* though this function allows different choices.
*/
NPY_NO_EXPORT PyArrayObject *
array_boolean_subscript(PyArrayObject *self,
PyArrayObject *bmask, NPY_ORDER order)
{
npy_intp size, itemsize;
char *ret_data;
PyArray_Descr *dtype;
PyArray_Descr *ret_dtype;
PyArrayObject *ret;
size = count_boolean_trues(PyArray_NDIM(bmask), PyArray_DATA(bmask),
PyArray_DIMS(bmask), PyArray_STRIDES(bmask));
/* Allocate the output of the boolean indexing */
dtype = PyArray_DESCR(self);
Py_INCREF(dtype);
ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, 1, &size,
NULL, NULL, 0, NULL);
if (ret == NULL) {
return NULL;
}
/* not same as *dtype* if the DType class replaces dtypes */
ret_dtype = PyArray_DESCR(ret);
itemsize = dtype->elsize;
ret_data = PyArray_DATA(ret);
/* Create an iterator for the data */
}
if (!PyArray_CheckExact(self)) {
PyArrayObject *tmp = ret;
Py_INCREF(ret_dtype);
ret = (PyArrayObject *)PyArray_NewFromDescrAndBase(
Py_TYPE(self), ret_dtype,
1, &size, PyArray_STRIDES(ret), PyArray_BYTES(ret),
PyArray_FLAGS(self), (PyObject *)self, (PyObject *)tmp);
Py_DECREF(tmp);
if (ret == NULL) {
return NULL;
}
}
return ret;
}
/*
* Implements boolean indexing assignment. This takes the one-dimensional
* array 'v' and assigns its values to all of the elements of 'self' for which
* the corresponding element of 'op' is True.
*
* This operation is somewhat unfortunate, because to match up with
* a one-dimensional output array, it has to choose a particular
* iteration order, in the case of NumPy that is always C order even
* though this function allows different choices.
*
* Returns 0 on success, -1 on failure.
*/
NPY_NO_EXPORT int
array_assign_boolean_subscript(PyArrayObject *self,
PyArrayObject *bmask, PyArrayObject *v, NPY_ORDER order)
{
npy_intp size, v_stride;
char *v_data;
npy_intp bmask_size;
if (PyArray_DESCR(bmask)->type_num != NPY_BOOL) {
PyErr_SetString(PyExc_TypeError,
"NumPy boolean array indexing assignment "
"requires a boolean index");
return -1;
}
}
// 检查输入数组 v 是否为多维数组(大于1维)
if (PyArray_NDIM(v) > 1) {
// 报错并返回 -1,要求 NumPy 布尔数组索引赋值需要输入为 0 或 1 维
PyErr_Format(PyExc_TypeError,
"NumPy boolean array indexing assignment "
"requires a 0 or 1-dimensional input, input "
"has %d dimensions", PyArray_NDIM(v));
return -1;
}
// 检查布尔掩码数组 bmask 的维度是否与被索引数组 self 的维度相同
if (PyArray_NDIM(bmask) != PyArray_NDIM(self)) {
// 报错并返回 -1,要求布尔掩码索引数组必须与被索引数组 self 的维度相同
PyErr_SetString(PyExc_ValueError,
"The boolean mask assignment indexing array "
"must have the same number of dimensions as "
"the array being indexed");
return -1;
}
// 计算布尔掩码中 True 的数量
size = count_boolean_trues(PyArray_NDIM(bmask), PyArray_DATA(bmask),
PyArray_DIMS(bmask), PyArray_STRIDES(bmask));
/* 用于调整广播 'bmask' 到 'self' 的修正因子 */
bmask_size = PyArray_SIZE(bmask);
if (bmask_size > 0) {
size *= PyArray_SIZE(self) / bmask_size;
}
// 调整用于 0 维和广播情况的步长
if (PyArray_NDIM(v) > 0 && PyArray_DIMS(v)[0] != 1) {
// 如果 v 是一维数组且长度与 size 不一致,则报错
if (size != PyArray_DIMS(v)[0]) {
PyErr_Format(PyExc_ValueError,
"NumPy boolean array indexing assignment "
"cannot assign %" NPY_INTP_FMT " input values to "
"the %" NPY_INTP_FMT " output values where the mask is true",
PyArray_DIMS(v)[0], size);
return -1;
}
// 获取 v 的步长
v_stride = PyArray_STRIDES(v)[0];
}
else {
// 对于 0 维数组,步长设为 0
v_stride = 0;
}
// 获取数组 v 的数据指针
v_data = PyArray_DATA(v);
/* 为数据创建迭代器 */
// 初始化结果变量为 0
int res = 0;
// 返回结果
return res;
}
/*
* C-level integer indexing always returning an array and never a scalar.
* Works also for subclasses, but it will not be called on one from the
* Python API.
*
* This function does not accept negative indices because it is called by
* PySequence_GetItem (through array_item) and that converts them to
* positive indices.
*/
NPY_NO_EXPORT PyObject *
array_item_asarray(PyArrayObject *self, npy_intp i)
{
npy_index_info indices[2]; // 定义索引信息结构体数组
PyObject *result; // 结果对象指针
if (PyArray_NDIM(self) == 0) {
PyErr_SetString(PyExc_IndexError,
"too many indices for array");
return NULL; // 如果数组维度为0,抛出索引错误并返回空
}
if (i < 0) {
/* This is an error, but undo PySequence_GetItem fix for message */
i -= PyArray_DIM(self, 0); // 如果索引为负数,将其转换为正数
}
indices[0].value = i; // 设置第一个索引的值为 i
indices[0].type = HAS_INTEGER; // 表示第一个索引是整数类型
indices[1].value = PyArray_NDIM(self) - 1; // 设置第二个索引的值为数组的最后一个维度
indices[1].type = HAS_ELLIPSIS; // 表示第二个索引是省略号类型
if (get_view_from_index(self, (PyArrayObject **)&result,
indices, 2, 0) < 0) {
return NULL; // 根据索引获取视图对象,失败则返回空
}
return result; // 返回获取的结果对象
}
/*
* Python C-Api level item subscription (implementation for PySequence_GetItem)
*
* Negative indices are not accepted because PySequence_GetItem converts
* them to positive indices before calling this.
*/
NPY_NO_EXPORT PyObject *
array_item(PyArrayObject *self, Py_ssize_t i)
{
if (PyArray_NDIM(self) == 1) { // 如果数组维度为1
char *item; // 字符指针 item
npy_index_info index; // 索引信息结构体
if (i < 0) {
/* This is an error, but undo PySequence_GetItem fix for message */
i -= PyArray_DIM(self, 0); // 如果索引为负数,将其转换为正数
}
index.value = i; // 设置索引值为 i
index.type = HAS_INTEGER; // 表示索引为整数类型
if (get_item_pointer(self, &item, &index, 1) < 0) {
return NULL; // 获取数组项指针,失败则返回空
}
return PyArray_Scalar(item, PyArray_DESCR(self), (PyObject *)self); // 根据数组项创建标量对象并返回
}
else {
return array_item_asarray(self, i); // 如果数组维度不为1,调用 array_item_asarray 函数处理
}
}
/* make sure subscript always returns an array object */
NPY_NO_EXPORT PyObject *
array_subscript_asarray(PyArrayObject *self, PyObject *op)
{
return PyArray_EnsureAnyArray(array_subscript(self, op)); // 确保使用下标访问时总是返回一个数组对象
}
/*
* Attempts to subscript an array using a field name or list of field names.
*
* ret = 0, view != NULL: view points to the requested fields of arr
* ret = 0, view == NULL: an error occurred
* ret = -1, view == NULL: unrecognized input, this is not a field index.
*/
NPY_NO_EXPORT int
_get_field_view(PyArrayObject *arr, PyObject *ind, PyArrayObject **view)
{
assert(PyDataType_ISLEGACY(PyArray_DESCR(arr))); // 断言数组的数据类型是遗留类型
*view = NULL; // 将视图对象指针初始化为空
/* first check for a single field name */
/* 检查是否为 Unicode 对象 */
if (PyUnicode_Check(ind)) {
PyObject *tup;
PyArray_Descr *fieldtype;
npy_intp offset;
/* 获取字段偏移量和数据类型 */
tup = PyDict_GetItemWithError(PyDataType_FIELDS(PyArray_DESCR(arr)), ind);
if (tup == NULL && PyErr_Occurred()) {
return 0;
}
else if (tup == NULL){
PyErr_Format(PyExc_ValueError, "no field of name %S", ind);
return 0;
}
if (_unpack_field(tup, &fieldtype, &offset) < 0) {
return 0;
}
/* 在新的偏移量和数据类型下查看数组 */
Py_INCREF(fieldtype);
*view = (PyArrayObject*)PyArray_NewFromDescr_int(
Py_TYPE(arr),
fieldtype,
PyArray_NDIM(arr),
PyArray_SHAPE(arr),
PyArray_STRIDES(arr),
PyArray_BYTES(arr) + offset,
PyArray_FLAGS(arr),
(PyObject *)arr, (PyObject *)arr,
/* 仅对字符串进行子数组时不保留数据类型 */
_NPY_ARRAY_ALLOW_EMPTY_STRING);
if (*view == NULL) {
return 0;
}
return 0;
}
/* 检查是否为字段名列表 */
else if (PySequence_Check(ind) && !PyTuple_Check(ind)) {
npy_intp seqlen, i;
PyArray_Descr *view_dtype;
seqlen = PySequence_Size(ind);
/* 如果是虚假的类似序列且调用 len() 时出错则退出 */
if (seqlen == -1) {
PyErr_Clear();
return -1;
}
/* 处理空列表作为整数索引的情况 */
if (seqlen == 0) {
return -1;
}
/* 检查所有项是否为字符串 */
for (i = 0; i < seqlen; i++) {
npy_bool is_string;
PyObject *item = PySequence_GetItem(ind, i);
if (item == NULL) {
PyErr_Clear();
return -1;
}
is_string = PyUnicode_Check(item);
Py_DECREF(item);
if (!is_string) {
return -1;
}
}
/* 调用 dtype 的下标操作 */
view_dtype = arraydescr_field_subset_view(
(_PyArray_LegacyDescr *)PyArray_DESCR(arr), ind);
if (view_dtype == NULL) {
return 0;
}
*view = (PyArrayObject*)PyArray_NewFromDescr_int(
Py_TYPE(arr),
view_dtype,
PyArray_NDIM(arr),
PyArray_SHAPE(arr),
PyArray_STRIDES(arr),
PyArray_DATA(arr),
PyArray_FLAGS(arr),
(PyObject *)arr, (PyObject *)arr,
/* 仅对字符串进行子数组时不保留数据类型 */
_NPY_ARRAY_ALLOW_EMPTY_STRING);
if (*view == NULL) {
return 0;
}
return 0;
}
/* 默认情况返回 -1 */
return -1;
}
/*
* General function for indexing a NumPy array with a Python object.
*/
NPY_NO_EXPORT PyObject *
array_subscript(PyArrayObject *self, PyObject *op)
{
// 定义索引类型、索引数量以及循环变量等
int index_type;
int index_num;
int i, ndim, fancy_ndim;
// 初始化类型为 NPY_cast_info 的结构体 cast_info,并设置 func 字段为 NULL
NPY_cast_info cast_info = {.func = NULL};
/*
* Index info array. We can have twice as many indices as dimensions
* (because of None). The + 1 is to not need to check as much.
*/
// 定义索引信息数组,数组大小为 NPY_MAXDIMS * 2 + 1,用于存储索引信息
npy_index_info indices[NPY_MAXDIMS * 2 + 1];
// 定义视图和结果对象,并初始化为 NULL
PyArrayObject *view = NULL;
PyObject *result = NULL;
// 定义 PyArrayMapIterObject 类型指针 mit,并初始化为 NULL
PyArrayMapIterObject * mit = NULL;
/* return fields if op is a string index */
// 如果 op 是字符串索引,并且数组描述符有字段信息
if (PyDataType_HASFIELDS(PyArray_DESCR(self))) {
// 声明视图对象 view,并调用 _get_field_view 函数
PyArrayObject *view;
// 调用 _get_field_view 函数尝试获取字段视图,结果存入 view
int ret = _get_field_view(self, op, &view);
// 如果 ret 等于 0,则成功获取视图
if (ret == 0){
// 如果视图为 NULL,返回 NULL
if (view == NULL) {
return NULL;
}
// 返回视图对象
return (PyObject*)view;
}
}
// 准备索引信息
index_type = prepare_index(self, op, indices, &index_num,
&ndim, &fancy_ndim, 1);
// 如果准备索引失败,返回 NULL
if (index_type < 0) {
return NULL;
}
// 如果索引类型是完全整数索引
else if (index_type == HAS_INTEGER) {
// 获取数组中元素的指针
char *item;
if (get_item_pointer(self, &item, indices, index_num) < 0) {
// 如果获取失败,跳转到 finish 标签
goto finish;
}
// 创建并返回数组标量对象
result = (PyObject *) PyArray_Scalar(item, PyArray_DESCR(self),
(PyObject *)self);
/* Because the index is full integer, we do not need to decref */
// 因为索引是完全整数,不需要减少引用计数
return result;
}
// 如果索引类型是布尔数组索引
else if (index_type == HAS_BOOL) {
// 使用布尔数组索引进行数组子脚本操作,并返回结果对象
result = (PyObject *)array_boolean_subscript(self,
(PyArrayObject *)indices[0].object,
NPY_CORDER);
// 跳转到 finish 标签
goto finish;
}
// 如果索引类型是单个省略号索引
else if (index_type == HAS_ELLIPSIS) {
/*
* TODO: Should this be a view or not? The only reason not would be
* optimization (i.e. of array[...] += 1) I think.
* Before, it was just self for a single ellipsis.
*/
// 创建并返回数组的视图对象
result = PyArray_View(self, NULL, NULL);
/* A single ellipsis, so no need to decref */
// 单个省略号索引,不需要减少引用计数
return result;
}
/*
* View based indexing.
* There are two cases here. First we need to create a simple view,
* second we need to create a (possibly invalid) view for the
* subspace to the fancy index. This procedure is identical.
*/
else if (index_type & (HAS_SLICE | HAS_NEWAXIS |
HAS_ELLIPSIS | HAS_INTEGER)) {
if (get_view_from_index(self, &view, indices, index_num,
(index_type & HAS_FANCY)) < 0) {
goto finish;
}
'''
* 存在标量数组,需要强制复制以模拟花式索引。
'''
if (index_type & HAS_SCALAR_ARRAY) {
result = PyArray_NewCopy(view, NPY_KEEPORDER);
goto finish;
}
}
'''
* 如果没有花式索引,直接使用视图对象作为结果。
'''
if (!(index_type & HAS_FANCY)) {
result = (PyObject *)view;
Py_INCREF(result);
goto finish;
}
'''
* 特殊情况:非常简单的一维花式索引,尽管如此常见。
* 这不仅节省了迭代器的设置时间,而且更快(必须完全是花式索引,
* 因为这里不支持0维布尔值)。
'''
if (index_type == HAS_FANCY && index_num == 1) {
/* 如果索引类型为 HAS_FANCY 并且索引数量为 1 */
PyArrayObject *ind = (PyArrayObject*)indices[0].object;
/* 将索引对象转换为 PyArrayObject 类型 */
if (PyArray_TRIVIALLY_ITERABLE(ind) &&
/* 检查索引是否足够简单 */
PyArray_ITEMSIZE(ind) == sizeof(npy_intp) &&
PyArray_DESCR(ind)->kind == 'i' &&
IsUintAligned(ind) &&
PyDataType_ISNOTSWAPPED(PyArray_DESCR(ind))) {
/* 检查类型是否等同于 INTP */
Py_INCREF(PyArray_DESCR(self));
/* 增加对数组描述符的引用计数 */
result = PyArray_NewFromDescr(&PyArray_Type,
PyArray_DESCR(self),
PyArray_NDIM(ind),
PyArray_SHAPE(ind),
NULL, NULL,
/* 与索引顺序相同 */
PyArray_ISFORTRAN(ind) ?
NPY_ARRAY_F_CONTIGUOUS : 0,
NULL);
/* 使用给定的描述符和形状创建新的数组对象 */
if (result == NULL) {
goto finish;
}
NPY_ARRAYMETHOD_FLAGS transfer_flags;
npy_intp itemsize = PyArray_ITEMSIZE(self);
/* 获取数组元素的大小 */
int is_aligned = IsUintAligned(self);
/* 检查数组是否按无符号整数对齐 */
if (PyArray_GetDTypeTransferFunction(is_aligned,
itemsize, itemsize,
PyArray_DESCR(self), PyArray_DESCR((PyArrayObject *)result),
0, &cast_info, &transfer_flags) != NPY_SUCCEED) {
goto finish;
}
if (mapiter_trivial_get(
self, ind, (PyArrayObject *)result, is_aligned, &cast_info) < 0) {
/* 调用 mapiter_trivial_get 函数获取数据 */
Py_DECREF(result);
result = NULL;
goto finish;
}
goto wrap_out_array;
/* 跳转到 wrap_out_array 标签处 */
}
}
/* 必须使用花式索引。视图是子空间。 */
mit = (PyArrayMapIterObject *)PyArray_MapIterNew(indices, index_num,
index_type,
ndim, fancy_ndim,
self, view, 0,
NPY_ITER_READONLY,
NPY_ITER_WRITEONLY,
NULL, PyArray_DESCR(self));
/* 创建数组映射迭代器对象 */
if (mit == NULL) {
goto finish;
}
if (mit->num_fancy > 1 || mit->size == 0) {
/*
* 如果 num_fancy 大于 1 或者 size 等于 0,
* 则需要进行内部循环检查索引;否则,
* 在广播发生时先进行索引检查,因为这样速度更快,
* 而且大多数情况下没有太大的开销。
* 然而,对于 size == 0 的情况,内部循环优化会跳过索引检查。
*/
if (PyArray_MapIterCheckIndices(mit) < 0) {
goto finish;
}
}
/* 重置外部迭代器 */
if (NpyIter_Reset(mit->outer, NULL) < 0) {
goto finish;
}
/*
* 对齐信息(由于我们使用缓冲区,不需要进行交换),
* 还可以检查 extra_op 是否已经缓冲,但这通常不重要。
*/
int is_aligned = IsUintAligned(self) && IsUintAligned(mit->extra_op);
/*
* 注意:获取数据类型转换函数时实际上不会执行类型转换,
* 因此我们目前不必进行完整的检查(例如浮点数错误)(不像赋值操作那样)。
*/
int meth_flags = NpyIter_GetTransferFlags(mit->outer);
if (mit->extra_op_iter) {
int extra_op_flags = NpyIter_GetTransferFlags(mit->extra_op_iter);
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, extra_op_flags);
}
if (mit->subspace_iter != NULL) {
int extra_op_flags = NpyIter_GetTransferFlags(mit->subspace_iter);
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, extra_op_flags);
NPY_ARRAYMETHOD_FLAGS transfer_flags;
npy_intp fixed_strides[2];
/*
* 获取 dtype 转换函数,由于没有缓冲区,这是安全的。
*/
NpyIter_GetInnerFixedStrideArray(mit->subspace_iter, fixed_strides);
if (PyArray_GetDTypeTransferFunction(is_aligned,
fixed_strides[0], fixed_strides[1],
PyArray_DESCR(self), PyArray_DESCR(mit->extra_op),
0, &cast_info, &transfer_flags) != NPY_SUCCEED) {
goto finish;
}
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, transfer_flags);
}
else {
/* 可能需要一个通用的复制函数(仅用于引用和奇怪的大小) */
NPY_ARRAYMETHOD_FLAGS transfer_flags;
npy_intp itemsize = PyArray_ITEMSIZE(self);
if (PyArray_GetDTypeTransferFunction(1,
itemsize, itemsize,
PyArray_DESCR(self), PyArray_DESCR(self),
0, &cast_info, &transfer_flags) != NPY_SUCCEED) {
goto finish;
}
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, transfer_flags);
}
if (mapiter_get(mit, &cast_info, meth_flags, is_aligned) < 0) {
goto finish;
}
result = (PyObject *)mit->extra_op;
Py_INCREF(result);
if (mit->consec) {
PyArray_MapIterSwapAxes(mit, (PyArrayObject **)&result, 1);
}
wrap_out_array:
if (!PyArray_CheckExact(self)) {
/*
* 如果 self 不是一个确切的 PyArray 对象,需要创建一个新的数组,
* 就好像旧数组从未存在过。
*/
PyArrayObject *tmp_arr = (PyArrayObject *)result;
// 增加临时数组的描述符的引用计数
Py_INCREF(PyArray_DESCR(tmp_arr));
// 使用临时数组的信息创建一个新的 PyArray 对象
result = PyArray_NewFromDescrAndBase(
Py_TYPE(self),
PyArray_DESCR(tmp_arr),
PyArray_NDIM(tmp_arr),
PyArray_SHAPE(tmp_arr),
PyArray_STRIDES(tmp_arr),
PyArray_BYTES(tmp_arr),
PyArray_FLAGS(tmp_arr),
(PyObject *)self, (PyObject *)tmp_arr);
Py_DECREF(tmp_arr);
// 如果创建失败,跳转至结束标签 finish
if (result == NULL) {
goto finish;
}
}
finish:
// 释放类型转换信息结构体的内存
NPY_cast_info_xfree(&cast_info);
// 释放 mit 对象的引用
Py_XDECREF(mit);
// 释放 view 对象的引用
Py_XDECREF(view);
/* 清理索引数组中的对象引用 */
for (i=0; i < index_num; i++) {
Py_XDECREF(indices[i].object);
}
// 返回 result 对象
return result;
/*
* Python C-Api level item assignment (implementation for PySequence_SetItem)
*
* Negative indices are not accepted because PySequence_SetItem converts
* them to positive indices before calling this.
*/
NPY_NO_EXPORT int
array_assign_item(PyArrayObject *self, Py_ssize_t i, PyObject *op)
{
npy_index_info indices[2]; // 定义索引信息结构体数组,用于存储索引信息
if (op == NULL) { // 如果操作对象为空指针
PyErr_SetString(PyExc_ValueError,
"cannot delete array elements"); // 设置错误消息:无法删除数组元素
return -1; // 返回错误代码
}
if (PyArray_FailUnlessWriteable(self, "assignment destination") < 0) {
return -1; // 如果数组不可写,则返回错误代码
}
if (PyArray_NDIM(self) == 0) { // 如果数组维度为0
PyErr_SetString(PyExc_IndexError,
"too many indices for array"); // 设置错误消息:数组索引过多
return -1; // 返回错误代码
}
if (i < 0) { // 如果索引值为负数
/* This is an error, but undo PySequence_SetItem fix for message */
i -= PyArray_DIM(self, 0); // 对负数索引进行修正
}
indices[0].value = i; // 设置第一个索引值
indices[0].type = HAS_INTEGER; // 设置索引类型为整数索引
if (PyArray_NDIM(self) == 1) { // 如果数组维度为1
char *item;
if (get_item_pointer(self, &item, indices, 1) < 0) {
return -1; // 获取数组元素指针失败,则返回错误代码
}
if (PyArray_Pack(PyArray_DESCR(self), item, op) < 0) {
return -1; // 将 Python 对象打包为数组元素失败,则返回错误代码
}
}
else { // 如果数组维度大于1
PyArrayObject *view;
indices[1].value = PyArray_NDIM(self) - 1; // 设置第二个索引值为最后一个维度索引
indices[1].type = HAS_ELLIPSIS; // 设置第二个索引类型为省略号索引
if (get_view_from_index(self, &view, indices, 2, 0) < 0) {
return -1; // 从索引获取视图对象失败,则返回错误代码
}
if (PyArray_CopyObject(view, op) < 0) {
Py_DECREF(view);
return -1; // 复制对象到视图对象失败,则返回错误代码
}
Py_DECREF(view);
}
return 0; // 返回成功代码
}
/* Full integer index */
// 如果索引类型为整数数组
if (index_type == HAS_INTEGER) {
char *item;
// 获取索引的指针
if (get_item_pointer(self, &item, indices, index_num) < 0) {
return -1;
}
// 将 item 打包到 self 中
if (PyArray_Pack(PyArray_DESCR(self), item, op) < 0) {
return -1;
}
/* integers do not store objects in indices */
// 整数索引不存储对象
return 0;
}
/* Single boolean array */
// 如果索引类型为布尔数组
if (index_type == HAS_BOOL) {
if (!PyArray_Check(op)) {
// 如果 op 不是数组,则创建一个新的数组 tmp_arr
Py_INCREF(PyArray_DESCR(self));
tmp_arr = (PyArrayObject *)PyArray_FromAny(op,
PyArray_DESCR(self), 0, 0,
NPY_ARRAY_FORCECAST, NULL);
if (tmp_arr == NULL) {
goto fail;
}
}
else {
// 如果 op 已经是数组,则直接增加引用计数
Py_INCREF(op);
tmp_arr = (PyArrayObject *)op;
}
// 使用布尔子脚本进行数组分配
if (array_assign_boolean_subscript(self,
(PyArrayObject *)indices[0].object,
tmp_arr, NPY_CORDER) < 0) {
goto fail;
}
// 成功时跳转到 success 标签
goto success;
}
/*
* Single ellipsis index, no need to create a new view.
* Note that here, we do *not* go through self.__getitem__ for subclasses
* (defchar array failed then, due to uninitialized values...)
*/
// 如果索引类型为省略号
else if (index_type == HAS_ELLIPSIS) {
if ((PyObject *)self == op) {
/*
* CopyObject does not handle this case gracefully and
* there is nothing to do. Removing the special case
* will cause segfaults, though it is unclear what exactly
* happens.
*/
// 如果 self 和 op 相同,则无需处理,直接返回
return 0;
}
/* we can just use self, but incref for error handling */
// 我们可以直接使用 self,但需要增加引用计数以处理错误
Py_INCREF((PyObject *)self);
view = self;
}
/*
* WARNING: There is a huge special case here. If this is not a
* base class array, we have to get the view through its
* very own index machinery.
* Many subclasses should probably call __setitem__
* with a base class ndarray view to avoid this.
*/
// 如果索引类型既不是花式索引也不是标量数组,并且 self 不是精确的数组类型
else if (!(index_type & (HAS_FANCY | HAS_SCALAR_ARRAY))
&& !PyArray_CheckExact(self)) {
// 通过索引获取视图
view = (PyArrayObject *)PyObject_GetItem((PyObject *)self, ind);
if (view == NULL) {
goto fail;
}
// 确保返回的是一个数组
if (!PyArray_Check(view)) {
PyErr_SetString(PyExc_RuntimeError,
"Getitem not returning array");
goto fail;
}
}
/*
* View based indexing.
* There are two cases here. First we need to create a simple view,
* second we need to create a (possibly invalid) view for the
* subspace to the fancy index. This procedure is identical.
*/
// 基于视图的索引操作
else if (index_type & (HAS_SLICE | HAS_NEWAXIS |
HAS_ELLIPSIS | HAS_INTEGER)) {
if (get_view_from_index(self, &view, indices, index_num,
(index_type & HAS_FANCY)) < 0) {
goto fail;
}
}
else {
view = NULL;
}
/* 如果没有花式索引,直接将视图拷贝给操作数 */
if (!(index_type & HAS_FANCY)) {
if (PyArray_CopyObject(view, op) < 0) {
goto fail;
}
goto success;
}
if (!PyArray_Check(op)) {
/*
* 如果数组是对象数组,并且操作数是一个序列,
* 尽管普通赋值可行,但转换值到数组可能不合法。
* 因此,分配一个正确大小的临时数组,并使用普通赋值处理这种情况。
*/
if (PyDataType_REFCHK(descr) && PySequence_Check(op)) {
tmp_arr = NULL;
}
else {
/* 没有可能使用花式索引,因此只需创建一个数组 */
Py_INCREF(descr);
tmp_arr = (PyArrayObject *)PyArray_FromAny(op, descr, 0, 0,
NPY_ARRAY_FORCECAST, NULL);
if (tmp_arr == NULL) {
goto fail;
}
}
}
else {
Py_INCREF(op);
tmp_arr = (PyArrayObject *)op;
}
/*
* 特殊情况处理非常简单的一维花式索引,这种情况相当常见。
* 这不仅节省了迭代器的设置时间,而且速度更快(必须完全是花式的,
* 因为这里不支持0维布尔值)
*/
if (index_type == HAS_FANCY &&
index_num == 1 && tmp_arr) {
/* 当索引类型为HAS_FANCY且索引数量为1,并且tmp_arr不为空时进入条件判断 */
PyArrayObject *ind = (PyArrayObject*)indices[0].object;
/* 将索引数组转换为PyArrayObject类型 */
/* 检查类型是否等效 */
if (PyArray_EquivTypes(PyArray_DESCR(self),
PyArray_DESCR(tmp_arr)) &&
/*
* 类型要么等效,要么值必须是标量
*/
(PyArray_EQUIVALENTLY_ITERABLE(ind, tmp_arr,
PyArray_TRIVIALLY_ITERABLE_OP_READ,
PyArray_TRIVIALLY_ITERABLE_OP_READ) ||
(PyArray_NDIM(tmp_arr) == 0 &&
PyArray_TRIVIALLY_ITERABLE(ind))) &&
/* 检查类型是否等效于INTP */
PyArray_ITEMSIZE(ind) == sizeof(npy_intp) &&
PyArray_DESCR(ind)->kind == 'i' &&
IsUintAligned(ind) &&
PyDataType_ISNOTSWAPPED(PyArray_DESCR(ind))) {
NPY_ARRAYMETHOD_FLAGS transfer_flags;
npy_intp itemsize = PyArray_ITEMSIZE(self);
int is_aligned = IsUintAligned(self) && IsUintAligned(tmp_arr);
if (PyArray_GetDTypeTransferFunction(is_aligned,
itemsize, itemsize,
PyArray_DESCR(self), PyArray_DESCR(self),
0, &cast_info, &transfer_flags) != NPY_SUCCEED) {
goto fail;
}
/* trivial_set函数检查索引,然后进行设置 */
if (mapiter_trivial_set(
self, ind, tmp_arr, is_aligned, &cast_info) < 0) {
goto fail;
}
goto success;
}
}
/*
* 注意:如果tmp_arr尚未分配,则应由mit处理分配。
* NPY_ITER_READWRITE 对于自动分配是必要的。
* Readwrite 模式不允许正确地进行广播,但这样的操作数总是具有完整的大小。
*/
mit = (PyArrayMapIterObject *)PyArray_MapIterNew(indices,
index_num, index_type,
ndim, fancy_ndim, self,
view, 0,
NPY_ITER_WRITEONLY,
((tmp_arr == NULL) ?
NPY_ITER_READWRITE :
NPY_ITER_READONLY),
tmp_arr, descr);
if (mit == NULL) {
goto fail;
}
if (tmp_arr == NULL) {
/* 如果 tmp_arr 为空指针,则需要填充额外的操作,首先需要交换 */
tmp_arr = mit->extra_op;
// 增加 tmp_arr 的引用计数,确保其不被释放
Py_INCREF(tmp_arr);
// 如果 mit->consec 为真,调用 PyArray_MapIterSwapAxes 函数交换轴
if (mit->consec) {
PyArray_MapIterSwapAxes(mit, &tmp_arr, 1);
// 如果交换后 tmp_arr 为空,跳转到 fail 标签处处理错误
if (tmp_arr == NULL) {
goto fail;
}
}
// 将 tmp_arr 的内容复制到 op 中
if (PyArray_CopyObject(tmp_arr, op) < 0) {
goto fail;
}
}
// 检查 MapIter 对象的索引是否有效
if (PyArray_MapIterCheckIndices(mit) < 0) {
goto fail;
}
/*
* 对齐信息(由于我们使用缓冲区,不需要交换),可以检查 extra_op 是否已缓冲,
* 但这通常很少有影响。
*/
int is_aligned = IsUintAligned(self) && IsUintAligned(mit->extra_op);
// 获取迭代器的传输标志
int meth_flags = NpyIter_GetTransferFlags(mit->outer);
// 如果存在额外操作的迭代器
if (mit->extra_op_iter) {
int extra_op_flags = NpyIter_GetTransferFlags(mit->extra_op_iter);
// 合并方法标志
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, extra_op_flags);
}
// 如果存在子空间迭代器
if (mit->subspace_iter != NULL) {
int extra_op_flags = NpyIter_GetTransferFlags(mit->subspace_iter);
// 合并方法标志
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, extra_op_flags);
NPY_ARRAYMETHOD_FLAGS transfer_flags;
npy_intp fixed_strides[2];
/*
* 获取 dtype 传输函数,由于没有缓冲区,这是安全的。
*/
NpyIter_GetInnerFixedStrideArray(mit->subspace_iter, fixed_strides);
// 获取数据类型的传输函数,设置转换标志
if (PyArray_GetDTypeTransferFunction(is_aligned,
fixed_strides[1], fixed_strides[0],
PyArray_DESCR(mit->extra_op), PyArray_DESCR(self),
0, &cast_info, &transfer_flags) != NPY_SUCCEED) {
goto fail;
}
// 合并方法标志
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, transfer_flags);
}
else {
/* 可能需要一个通用的复制函数(仅适用于引用和奇怪的大小) */
NPY_ARRAYMETHOD_FLAGS transfer_flags;
npy_intp itemsize = PyArray_ITEMSIZE(self);
// 获取数据类型的传输函数,设置转换标志
if (PyArray_GetDTypeTransferFunction(1,
itemsize, itemsize,
PyArray_DESCR(self), PyArray_DESCR(self),
0, &cast_info, &transfer_flags) != NPY_SUCCEED) {
goto fail;
}
// 合并方法标志
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, transfer_flags);
}
// 如果方法标志不包含 NPY_METH_NO_FLOATINGPOINT_ERRORS,则清除浮点状态
if (!(meth_flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) {
npy_clear_floatstatus_barrier((char *)mit);
}
// 现在可以重置外部迭代器(延迟 bufalloc)
if (NpyIter_Reset(mit->outer, NULL) < 0) {
goto fail;
}
/*
* 可能需要一个类型转换检查,但显然大多数赋值操作不关心安全转换。
*/
// 如果设置 MapIter 的函数失败,则跳转到 fail 处理错误
if (mapiter_set(mit, &cast_info, meth_flags, is_aligned) < 0) {
goto fail;
}
// 如果方法标志不包含 NPY_METH_NO_FLOATINGPOINT_ERRORS,则获取浮点错误状态
if (!(meth_flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) {
int fpes = npy_get_floatstatus_barrier((char *)mit);
// 如果获取到浮点错误并且处理浮点错误失败,则跳转到 fail 处理错误
if (fpes && PyUFunc_GiveFloatingpointErrors("cast", fpes) < 0) {
goto fail;
}
// 如果 tmp_arr 为空指针,则需要填充额外的操作,首先需要交换
if (tmp_arr == NULL) {
tmp_arr = mit->extra_op;
// 增加 tmp_arr 的引用计数,确保其不被释放
Py_INCREF(tmp_arr);
// 如果 mit->consec 为真,调用 PyArray_MapIterSwapAxes 函数交换轴
if (mit->consec) {
PyArray_MapIterSwapAxes(mit, &tmp_arr, 1);
// 如果交换后 tmp_arr 为空,跳转到 fail 标签处处理错误
if (tmp_arr == NULL) {
goto fail;
}
}
// 将 tmp_arr 的内容复制到 op 中
if (PyArray_CopyObject(tmp_arr, op) < 0) {
goto fail;
}
}
// 检查 MapIter 对象的索引是否有效
if (PyArray_MapIterCheckIndices(mit) < 0) {
goto fail;
}
/*
* 对齐信息(由于我们使用缓冲区,不需要交换),可以检查 extra_op 是否已缓冲,
* 但这通常很少有影响。
*/
int is_aligned = IsUintAligned(self) && IsUintAligned(mit->extra_op);
// 获取迭代器的传输标志
int meth_flags = NpyIter_GetTransferFlags(mit->outer);
// 如果存在额外操作的迭代器
if (mit->extra_op_iter) {
int extra_op_flags = NpyIter_GetTransferFlags(mit->extra_op_iter);
// 合并方法标志
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, extra_op_flags);
}
// 如果存在子空间迭代器
if (mit->subspace_iter != NULL) {
int extra_op_flags = NpyIter_GetTransferFlags(mit->subspace_iter);
// 合并方法标志
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, extra_op_flags);
NPY_ARRAYMETHOD_FLAGS transfer_flags;
npy_intp fixed_strides[2];
/*
* 获取 dtype 传输函数,由于没有缓冲区,这是安全的。
*/
NpyIter_GetInnerFixedStrideArray(mit->subspace_iter, fixed_strides);
// 获取数据类型的传输函数,设置转换标志
if (PyArray_GetDTypeTransferFunction(is_aligned,
fixed_strides[1], fixed_strides[0],
PyArray_DESCR(mit->extra_op), PyArray_DESCR(self),
0, &cast_info, &transfer_flags) != NPY_SUCCEED) {
goto fail;
}
// 合并方法标志
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, transfer_flags);
}
else {
/* 可能需要一个通用的复制函数(仅适用于引用和奇怪的大小) */
NPY_ARRAYMETHOD_FLAGS transfer_flags;
npy_intp itemsize = PyArray_ITEMSIZE(self);
// 获取数据类型的传输函数,设置转换标志
if (PyArray_GetDTypeTransferFunction(1,
itemsize, itemsize,
PyArray_DESCR(self), PyArray_DESCR(self),
0, &cast_info, &transfer_flags) != NPY_SUCCEED) {
goto fail;
}
// 合并方法标志
meth_flags = PyArrayMethod_COMBINED_FLAGS(meth_flags, transfer_flags);
}
// 如果方法标志不包含 NPY_METH_NO_FLOATINGPOINT_ERRORS,则清除浮点状态
if (!(meth_flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) {
npy_clear_floatstatus_barrier((char *)mit);
}
// 现在可以重置外部迭代器(延迟 bufalloc)
if (NpyIter_Reset(mit->outer, NULL) < 0) {
goto fail;
}
/*
* 可能需要一个类型转换检查,但显然大多数赋值操作不关心安全转换。
*/
// 如果设置 MapIter 的函数失败,则跳转到 fail 处理错误
if (mapiter_set(mit, &cast_info, meth_flags, is_aligned) < 0) {
goto fail;
}
// 如果方法标志不包含 NPY_METH_NO_FLOATINGPOINT_ERRORS,则获取浮点错误状态
if (!(meth_flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) {
int fpes = npy_get_floatstatus_barrier((char *)mit);
// 如果获取到浮点错误并且处理浮点错误失败,则跳转到 fail 处理错误
if (fpes && PyUFunc_GiveFloatingpointErrors("cast", fpes) < 0) {
goto fail;
}
}
}
// 减少 mit 的引用计数,释放其占用的内存
Py_DECREF(mit);
// 跳转到成功的标签,表示函数成功执行
goto success;
/* Clean up temporary variables and indices */
fail:
// 释放视图对象的内存
Py_XDECREF((PyObject *)view);
// 释放临时数组对象的内存
Py_XDECREF((PyObject *)tmp_arr);
// 释放 mit 对象的内存
Py_XDECREF((PyObject *)mit);
// 释放类型转换信息结构体的内存
NPY_cast_info_xfree(&cast_info);
// 循环释放索引数组中每个元素对象的内存
for (i=0; i < index_num; i++) {
Py_XDECREF(indices[i].object);
}
// 返回 -1 表示执行失败
return -1;
success:
// 释放视图对象的内存
Py_XDECREF((PyObject *)view);
// 释放临时数组对象的内存
Py_XDECREF((PyObject *)tmp_arr);
// 释放类型转换信息结构体的内存
NPY_cast_info_xfree(&cast_info);
// 循环释放索引数组中每个元素对象的内存
for (i=0; i < index_num; i++) {
Py_XDECREF(indices[i].object);
}
// 返回 0 表示执行成功
return 0;
/*********************** Subscript Array Iterator *************************
* *
* This object handles subscript behavior for array objects. *
* It is an iterator object with a next method *
* It abstracts the n-dimensional mapping behavior to make the looping *
* code more understandable (maybe) *
* and so that indexing can be set up ahead of time *
*/
/*
* This function takes a Boolean array and constructs index objects and
* iterators as if nonzero(Bool) had been called
*
* Must not be called on a 0-d array.
*/
static int
_nonzero_indices(PyObject *myBool, PyArrayObject **arrays)
{
PyArray_Descr *typecode;
PyArrayObject *ba = NULL, *new = NULL;
int nd, j;
npy_intp size, i, count;
npy_bool *ptr;
npy_intp coords[NPY_MAXDIMS], dims_m1[NPY_MAXDIMS];
npy_intp *dptr[NPY_MAXDIMS];
static npy_intp one = 1;
NPY_BEGIN_THREADS_DEF;
// 创建一个描述符类型为 NPY_BOOL 的 PyArray_Descr 对象
typecode = PyArray_DescrFromType(NPY_BOOL);
// 从任意对象 myBool 创建一个布尔数组 ba
ba = (PyArrayObject *)PyArray_FromAny(myBool, typecode, 0, 0,
NPY_ARRAY_CARRAY, NULL);
// 如果创建失败,返回错误码 -1
if (ba == NULL) {
return -1;
}
// 获取数组 ba 的维度数
nd = PyArray_NDIM(ba);
// 将数组指针初始化为空
for (j = 0; j < nd; j++) {
arrays[j] = NULL;
}
// 获取数组 ba 的总大小
size = PyArray_SIZE(ba);
// 获取数组 ba 的数据指针,并转换为布尔型指针
ptr = (npy_bool *)PyArray_DATA(ba);
/*
* 预先确定有多少个非零条目,
* 忽略输入的维度信息,因为它是一个 CARRAY
*/
// 调用 count_boolean_trues 函数计算布尔数组中的真值数量
count = count_boolean_trues(1, (char*)ptr, &size, &one);
/* 为每个维度创建大小为 count 的索引数组 */
for (j = 0; j < nd; j++) {
// 创建一个新的 intp 类型数组 new,大小为 count
new = (PyArrayObject *)PyArray_NewFromDescr(
&PyArray_Type, PyArray_DescrFromType(NPY_INTP),
1, &count, NULL, NULL,
0, NULL);
// 如果创建失败,跳转到失败标签
if (new == NULL) {
goto fail;
}
// 将新创建的数组赋值给 arrays[j]
arrays[j] = new;
// 获取新数组 new 的数据指针,并转换为 intp 型指针
dptr[j] = (npy_intp *)PyArray_DATA(new);
// 初始化坐标数组的当前维度为 0
coords[j] = 0;
// 将 ba 在当前维度上的最大索引保存到 dims_m1 数组中
dims_m1[j] = PyArray_DIMS(ba)[j] - 1;
}
// 如果 count 为 0,直接跳转到结束标签
if (count == 0) {
goto finish;
}
/*
* 遍历布尔数组,并复制非零条目的坐标
*/
// 启动多线程处理,根据 size 的大小决定是否启动多线程
NPY_BEGIN_THREADS_THRESHOLDED(size);
// 循环遍历从 0 到 size-1 的索引 i
for (i = 0; i < size; i++) {
// 检查 ptr 指向的值是否为真,如果是则执行内部代码块
if (*(ptr++)) {
// 遍历从 0 到 nd-1 的索引 j
for (j = 0; j < nd; j++) {
// 将 coords[j] 的值赋给 dptr[j] 所指向的位置,并移动 dptr[j] 指针
*(dptr[j]++) = coords[j];
}
}
/* Borrowed from ITER_NEXT macro */
// 从 nd-1 开始到 0 的索引 j 进行遍历
for (j = nd - 1; j >= 0; j--) {
// 如果 coords[j] 小于 dims_m1[j],则将 coords[j] 增加 1 并跳出循环
if (coords[j] < dims_m1[j]) {
coords[j]++;
break;
}
// 否则将 coords[j] 设为 0
else {
coords[j] = 0;
}
}
}
NPY_END_THREADS;
finish:
// 释放 ba 对象的引用
Py_DECREF(ba);
// 返回 nd 的值
return nd;
fail:
// 失败时释放所有 arrays[j] 的引用
for (j = 0; j < nd; j++) {
Py_XDECREF(arrays[j]);
}
// 释放 ba 对象的引用
Py_XDECREF(ba);
// 返回 -1 表示失败
return -1;
/* 重置映射迭代器到开始位置 */
NPY_NO_EXPORT int
PyArray_MapIterReset(PyArrayMapIterObject *mit)
{
npy_intp indval; // 定义整数索引值
char *baseptrs[2]; // 定义两个字符指针数组
int i; // 定义循环计数变量
if (mit->size == 0) { // 如果迭代器大小为0,则直接返回0
return 0;
}
if (!NpyIter_Reset(mit->outer, NULL)) { // 重置外部迭代器,如果失败则返回-1
return -1;
}
if (mit->extra_op_iter) { // 如果存在额外操作迭代器
if (!NpyIter_Reset(mit->extra_op_iter, NULL)) { // 重置额外操作迭代器,如果失败则返回-1
return -1;
}
baseptrs[1] = mit->extra_op_ptrs[0]; // 设置第二个基础指针
}
baseptrs[0] = mit->baseoffset; // 设置第一个基础指针为偏移量
for (i = 0; i < mit->num_fancy; i++) { // 遍历所有的特殊索引
indval = *((npy_intp*)mit->outer_ptrs[i]); // 获取外部指针的索引值
if (indval < 0) { // 如果索引值小于0,则加上特殊维度值
indval += mit->fancy_dims[i];
}
baseptrs[0] += indval * mit->fancy_strides[i]; // 根据索引值和步幅计算基础指针偏移量
}
mit->dataptr = baseptrs[0]; // 设置数据指针为第一个基础指针
if (mit->subspace_iter) { // 如果存在子空间迭代器
if (!NpyIter_ResetBasePointers(mit->subspace_iter, baseptrs, NULL)) { // 重置子空间迭代器的基础指针,如果失败则返回-1
return -1;
}
mit->iter_count = *NpyIter_GetInnerLoopSizePtr(mit->subspace_iter); // 获取子空间迭代器的内部循环大小
}
else {
mit->iter_count = *NpyIter_GetInnerLoopSizePtr(mit->outer); // 否则获取外部迭代器的内部循环大小
}
return 0; // 返回0表示成功
}
/*
* 这个函数需要更新映射迭代器的状态,并将 mit->dataptr 指向下一个对象的内存位置
*
* 需要注意,此函数不处理额外操作数,但为旧的(已暴露的)API提供兼容性。
*/
NPY_NO_EXPORT void
PyArray_MapIterNext(PyArrayMapIterObject *mit)
{
int i; // 定义循环计数变量
char *baseptr; // 定义字符指针变量
npy_intp indval; // 定义整数索引值
if (mit->subspace_iter) { // 如果存在子空间迭代器
if (--mit->iter_count > 0) { // 如果内部循环计数大于0
mit->subspace_ptrs[0] += mit->subspace_strides[0]; // 子空间指针加上子空间步幅
mit->dataptr = mit->subspace_ptrs[0]; // 设置数据指针为子空间指针
return;
}
else if (mit->subspace_next(mit->subspace_iter)) { // 否则如果子空间迭代器的下一个有效
mit->iter_count = *NpyIter_GetInnerLoopSizePtr(mit->subspace_iter); // 获取子空间迭代器的内部循环大小
mit->dataptr = mit->subspace_ptrs[0]; // 设置数据指针为子空间指针
}
else { // 否则
if (!mit->outer_next(mit->outer)) { // 如果外部迭代器的下一个无效,则返回
return;
}
baseptr = mit->baseoffset; // 设置基础指针为偏移量
for (i = 0; i < mit->num_fancy; i++) { // 遍历所有的特殊索引
indval = *((npy_intp*)mit->outer_ptrs[i]); // 获取外部指针的索引值
if (indval < 0) { // 如果索引值小于0,则加上特殊维度值
indval += mit->fancy_dims[i];
}
baseptr += indval * mit->fancy_strides[i]; // 根据索引值和步幅计算基础指针偏移量
}
NpyIter_ResetBasePointers(mit->subspace_iter, &baseptr, NULL); // 重置子空间迭代器的基础指针
mit->iter_count = *NpyIter_GetInnerLoopSizePtr(mit->subspace_iter); // 获取子空间迭代器的内部循环大小
mit->dataptr = mit->subspace_ptrs[0]; // 设置数据指针为子空间指针
}
}
else {
// 如果迭代计数大于0,执行以下操作
if (--mit->iter_count > 0) {
// 将基础指针设置为迭代器的基础偏移量
baseptr = mit->baseoffset;
// 遍历所有的“花式”索引
for (i = 0; i < mit->num_fancy; i++) {
// 增加外部指针的步长
mit->outer_ptrs[i] += mit->outer_strides[i];
// 读取当前外部指针的整数值
indval = *((npy_intp*)mit->outer_ptrs[i]);
// 如果该值小于0,加上“花式”维度的大小
if (indval < 0) {
indval += mit->fancy_dims[i];
}
// 基础指针加上计算出的索引值乘以“花式”步长
baseptr += indval * mit->fancy_strides[i];
}
// 设置数据指针为新的基础指针位置,并返回
mit->dataptr = baseptr;
return;
}
else {
// 如果迭代计数不大于0,检查是否可以获取下一个外部迭代器
if (!mit->outer_next(mit->outer)) {
// 如果无法获取下一个外部迭代器,直接返回
return;
}
// 重新设置迭代计数为当前外部迭代器的内部循环大小
mit->iter_count = *NpyIter_GetInnerLoopSizePtr(mit->outer);
// 将基础指针设置为迭代器的基础偏移量
baseptr = mit->baseoffset;
// 再次遍历所有的“花式”索引
for (i = 0; i < mit->num_fancy; i++) {
// 读取当前外部指针的整数值
indval = *((npy_intp*)mit->outer_ptrs[i]);
// 如果该值小于0,加上“花式”维度的大小
if (indval < 0) {
indval += mit->fancy_dims[i];
}
// 基础指针加上计算出的索引值乘以“花式”步长
baseptr += indval * mit->fancy_strides[i];
}
// 设置数据指针为新的基础指针位置
mit->dataptr = baseptr;
}
}
/**
* Fill information about the iterator. The MapIterObject does not
* need to have any information set for this function to work.
* (PyArray_MapIterSwapAxes requires also nd and nd_fancy info)
*
* Sets the following information:
* * mit->consec: The axis where the fancy indices need transposing to.
* * mit->iteraxes: The axis which the fancy index corresponds to.
* * mit->fancy_dims: the dimension of `arr` along the indexed dimension
* for each fancy index.
* * mit->fancy_strides: the strides for the dimension being indexed
* by each fancy index.
* * mit->dimensions: Broadcast dimension of the fancy indices and
* the subspace iteration dimension.
*
* @param MapIterObject The iterator object to fill with information.
* @param indices The parsed indices object containing index information.
* @param index_num Number of indices.
* @param arr The array that is being iterated.
*
* @return 0 on success, -1 on failure (broadcasting or too many fancy indices).
*/
static int
mapiter_fill_info(PyArrayMapIterObject *mit, npy_index_info *indices,
int index_num, PyArrayObject *arr)
{
int j = 0, i;
int curr_dim = 0;
/* dimension of index result (up to first fancy index) */
int result_dim = 0;
/* -1 init; 0 found fancy; 1 fancy stopped; 2 found not consecutive fancy */
int consec_status = -1;
int axis, broadcast_axis;
npy_intp dimension;
// Initialize dimensions of fancy indices to 1
for (i = 0; i < mit->nd_fancy; i++) {
mit->dimensions[i] = 1;
}
// Set consec to 0
mit->consec = 0;
// Fill dimension of subspace if it exists
if (mit->subspace) {
for (i = 0; i < PyArray_NDIM(mit->subspace); i++) {
mit->dimensions[mit->nd_fancy + i] = PyArray_DIM(mit->subspace, i);
}
}
// Successful completion
return 0;
broadcast_error: ; // Declarations cannot follow labels, add empty statement.
/*
* Attempt to set a meaningful exception. Could also find out
* if a boolean index was converted.
*/
// Initialize error message
PyObject *errmsg = PyUnicode_FromString("");
if (errmsg == NULL) {
return -1;
}
// Iterate over indices to build error message
for (i = 0; i < index_num; i++) {
// Skip non-fancy indices
if (!(indices[i].type & HAS_FANCY)) {
continue;
}
// Get dimensions and shape of the index object
int ndim = PyArray_NDIM((PyArrayObject *)indices[i].object);
npy_intp *shape = PyArray_SHAPE((PyArrayObject *)indices[i].object);
// Convert shape information to string
PyObject *tmp = convert_shape_to_string(ndim, shape, " ");
if (tmp == NULL) {
Py_DECREF(errmsg);
return -1;
}
// Concatenate shape information to error message
Py_SETREF(errmsg, PyUnicode_Concat(errmsg, tmp));
Py_DECREF(tmp);
if (errmsg == NULL) {
return -1;
}
}
// Format and set PyErr exception for broadcasting error
PyErr_Format(PyExc_IndexError,
"shape mismatch: indexing arrays could not "
"be broadcast together with shapes %S", errmsg);
Py_DECREF(errmsg);
return -1;
}
def PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit):
PyArrayObject *op;
NpyIter *op_iter;
NpyIter_IterNextFunc *op_iternext;
npy_intp outer_dim, indval;
int outer_axis;
npy_intp itersize, *iterstride;
char **iterptr;
PyArray_Descr *intp_type;
int i;
if (NpyIter_GetIterSize(mit->outer) == 0) {
"""
* 当外层迭代为空时,索引广播到一个空形状,此时我们不检查是否存在越界索引。
* 下面的代码在不进行广播的情况下使用索引,因为广播只是重复值。
"""
return 0;
}
intp_type = PyArray_DescrFromType(NPY_INTP);
NPY_BEGIN_THREADS;
// 遍历mit结构体中的每个fancy索引操作
for (i=0; i < mit->num_fancy; i++) {
// 获取当前操作数
op = NpyIter_GetOperandArray(mit->outer)[i];
// 获取外部维度和迭代轴
outer_dim = mit->fancy_dims[i];
outer_axis = mit->iteraxes[i];
/* 查看是否可以简单迭代数组 */
if (PyArray_TRIVIALLY_ITERABLE(op) &&
/* 检查类型是否等同于INTP */
PyArray_ITEMSIZE(op) == sizeof(npy_intp) &&
PyArray_DESCR(op)->kind == 'i' &&
IsUintAligned(op) &&
PyDataType_ISNOTSWAPPED(PyArray_DESCR(op))) {
char *data;
npy_intp stride;
/* 如果GIL被下面的nditer占用,则释放它 */
if (_save == NULL) {
NPY_BEGIN_THREADS;
}
// 准备简单迭代操作
PyArray_PREPARE_TRIVIAL_ITERATION(op, itersize, data, stride);
// 迭代操作数据
while (itersize--) {
indval = *((npy_intp*)data);
// 检查并调整索引值
if (check_and_adjust_index(&indval,
outer_dim, outer_axis, _save) < 0) {
Py_DECREF(intp_type);
// 跳转到索引错误处理
goto indexing_error;
}
data += stride;
}
/* 在函数结束或需要nditer路径时重新获取GIL */
continue;
}
/* 如果无法简单迭代,则使用NpyIter */
NPY_END_THREADS;
// 创建NpyIter对象
op_iter = NpyIter_New(op,
NPY_ITER_BUFFERED | NPY_ITER_NBO | NPY_ITER_ALIGNED |
NPY_ITER_EXTERNAL_LOOP | NPY_ITER_GROWINNER |
NPY_ITER_READONLY | NPY_ITER_ZEROSIZE_OK,
NPY_KEEPORDER, NPY_SAME_KIND_CASTING, intp_type);
// 检查NpyIter对象是否创建成功
if (op_iter == NULL) {
Py_DECREF(intp_type);
return -1;
}
// 如果迭代大小为0,则释放NpyIter对象并继续下一次循环
if (NpyIter_GetIterSize(op_iter) == 0) {
NpyIter_Deallocate(op_iter);
continue;
}
// 获取NpyIter对象的迭代器和数据指针
op_iternext = NpyIter_GetIterNext(op_iter, NULL);
if (op_iternext == NULL) {
Py_DECREF(intp_type);
NpyIter_Deallocate(op_iter);
return -1;
}
// 开始多线程迭代操作
NPY_BEGIN_THREADS_NDITER(op_iter);
iterptr = NpyIter_GetDataPtrArray(op_iter);
iterstride = NpyIter_GetInnerStrideArray(op_iter);
do {
// 获取内部循环的大小
itersize = *NpyIter_GetInnerLoopSizePtr(op_iter);
while (itersize--) {
indval = *((npy_intp*)*iterptr);
// 检查并调整索引值
if (check_and_adjust_index(&indval,
outer_dim, outer_axis, _save) < 0) {
Py_DECREF(intp_type);
NpyIter_Deallocate(op_iter);
// 跳转到索引错误处理
goto indexing_error;
}
*iterptr += *iterstride;
}
} while (op_iternext(op_iter));
// 结束多线程迭代
NPY_END_THREADS;
// 释放NpyIter对象
NpyIter_Deallocate(op_iter);
}
// 结束多线程迭代
NPY_END_THREADS;
// 释放intp_type类型对象的引用计数
Py_DECREF(intp_type);
// 返回成功状态
return 0;
indexing_error:
if (mit->size == 0) {
// 检查迭代器的大小是否为0,即迭代结果中没有元素
PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL;
// 检索最近的 Python 异常
PyErr_Fetch(&err_type, &err_value, &err_traceback);
/* 2020-05-27, NumPy 1.20 */
// 发出弃用警告,说明索引越界将不再被忽略,而是引发异常
if (DEPRECATE(
"Out of bound index found. This was previously ignored "
"when the indexing result contained no elements. "
"In the future the index error will be raised. This error "
"occurs either due to an empty slice, or if an array has zero "
"elements even before indexing.\n"
"(Use `warnings.simplefilter('error')` to turn this "
"DeprecationWarning into an error and get more details on "
"the invalid index.)") < 0) {
// 将之前检索到的异常链入当前 Python 异常
npy_PyErr_ChainExceptions(err_type, err_value, err_traceback);
return -1;
}
// 释放异常对象的引用
Py_DECREF(err_type);
Py_DECREF(err_value);
Py_XDECREF(err_traceback);
return 0;
}
return -1;
}
/*
* Create new mapiter.
*
* NOTE: The outer iteration (and subspace if requested buffered) is
* created with DELAY_BUFALLOC. It must be reset before usage!
*
* @param Index information filled by prepare_index.
* 由 prepare_index 填充的索引信息
* @param Number of indices (gotten through prepare_index).
* 索引的数量(通过 prepare_index 获得)
* @param Kind of index (gotten through preprare_index).
* 索引的类型(通过 prepare_index 获得)
* @param NpyIter flags for an extra array. If 0 assume that there is no
* extra operand. NPY_ITER_ALLOCATE can make sense here.
* 用于额外数组的 NpyIter 标志。如果为 0,则假设没有额外操作数。
* NPY_ITER_ALLOCATE 在这里可能有意义。
* @param Array being indexed
* 被索引的数组
* @param subspace (result of getting view for the indices)
* subspace(通过索引获取视图的结果)
* @param Subspace iterator flags can be used to enable buffering.
* NOTE: When no subspace is necessary, the extra operand will
* always be buffered! Buffering the subspace when not
* necessary is very slow when the subspace is small.
* 子空间迭代器标志可用于启用缓冲。
* 注意:当不需要子空间时,额外操作数始终会被缓冲!
* 当子空间很小时,缓冲子空间不必要地慢。
* @param Subspace operand flags (should just be 0 normally)
* 子空间操作数标志(通常应为0)
* @param Operand iteration flags for the extra operand, this must not be
* 0 if an extra operand should be used, otherwise it must be 0.
* Should be at least READONLY, WRITEONLY or READWRITE.
* 用于额外操作数的操作数迭代标志,如果应使用额外操作数,则此值不能为0,否则必须为0。
* 应至少为 READONLY、WRITEONLY 或 READWRITE。
* @param Extra operand. For getmap, this would be the result, for setmap
* this would be the arrays to get from.
* Can be NULL, and will be allocated in that case. However,
* it matches the mapiter iteration, so you have to call
* MapIterSwapAxes(mit, &extra_op, 1) on it.
* The operand has no effect on the shape.
* 额外的操作数。对于 getmap,这将是结果,对于 setmap,这将是要获取的数组。
* 可以为 NULL,在这种情况下将会分配内存。但是,它匹配 mapiter 迭代,因此您必须在其上调用 MapIterSwapAxes(mit, &extra_op, 1)。
* 操作数对形状没有影响。
* @param Dtype for the extra operand, borrows the reference and must not
* be NULL (if extra_op_flags is not 0).
* 用于额外操作数的 Dtype,借用引用并且不能为 NULL(如果 extra_op_flags 不为 0)。
*
* @return A new MapIter (PyObject *) or NULL.
* 返回新的 MapIter(PyObject *)或 NULL。
*/
NPY_NO_EXPORT PyObject *
/* 用于在错误报告中报告形状 */
PyArrayObject *original_extra_op = extra_op;
/* 注意:MAXARGS 是实际限制(2*NPY_MAXDIMS 是第一个索引号) */
PyArrayObject *index_arrays[NPY_MAXDIMS];
PyArray_Descr *intp_descr;
PyArray_Descr *dtypes[NPY_MAXDIMS]; /* 借用的引用 */
npy_uint32 op_flags[NPY_MAXDIMS];
npy_uint32 outer_flags;
PyArrayMapIterObject *mit;
int single_op_axis[NPY_MAXDIMS];
int *op_axes[NPY_MAXDIMS] = {NULL};
int i, j, dummy_array = 0;
int nops;
int uses_subspace;
/* 从 NPY_INTP 类型创建描述符 */
intp_descr = PyArray_DescrFromType(NPY_INTP);
if (intp_descr == NULL) {
return NULL;
}
/* 创建新的 MapIter 对象 */
mit = (PyArrayMapIterObject *)PyArray_malloc(
sizeof(PyArrayMapIterObject) + sizeof(NPY_cast_info));
if (mit == NULL) {
Py_DECREF(intp_descr);
return NULL;
}
/* 将 mapiter 的所有属性设置为零 */
memset(mit, 0, sizeof(PyArrayMapIterObject) + sizeof(NPY_cast_info));
PyObject_Init((PyObject *)mit, &PyArrayMapIter_Type);
Py_INCREF(arr);
mit->array = arr;
Py_XINCREF(subspace);
mit->subspace = subspace;
/*
* subspace 是数组未索引部分,在 subspace 大小大于 1 时需要迭代。
* 如果大小为 1,则仅影响结果形状。(优化例如 np.newaxis 的用法)
*/
if ((subspace == NULL) || PyArray_SIZE(subspace) == 1) {
uses_subspace = 0;
}
else {
uses_subspace = 1;
}
/* 填充 mapiter 的基本信息 */
mit->nd = ndim;
mit->nd_fancy = fancy_ndim;
if (mapiter_fill_info(mit, indices, index_num, arr) < 0) {
Py_DECREF(mit);
Py_DECREF(intp_descr);
return NULL;
}
/*
* 设置索引数组的迭代信息。
*/
for (i=0; i < index_num; i++) {
if (indices[i].type & HAS_FANCY) {
index_arrays[mit->num_fancy] = (PyArrayObject *)indices[i].object;
dtypes[mit->num_fancy] = intp_descr;
op_flags[mit->num_fancy] = (NPY_ITER_NBO |
NPY_ITER_ALIGNED |
NPY_ITER_READONLY);
mit->num_fancy += 1;
}
}
if (mit->num_fancy == 0) {
/*
* 对于 MapIterArray,可能没有使用 fancy 索引。
* 为了支持这种情况,添加一个虚拟迭代器。
* 由于它是零维的,其转置等操作并不重要。
*/
/* 信号需要减少引用计数... */
dummy_array = 1;
// 创建一个包含零个元素的整数类型数组作为索引数组
index_arrays[0] = (PyArrayObject *)PyArray_Zeros(0, NULL,
PyArray_DescrFromType(NPY_INTP), 0);
// 如果创建失败,释放资源并返回空指针
if (index_arrays[0] == NULL) {
Py_DECREF(mit);
Py_DECREF(intp_descr);
return NULL;
}
// 设置数据类型为整数类型描述符
dtypes[0] = intp_descr;
// 设置操作标志为字节序、对齐和只读
op_flags[0] = NPY_ITER_NBO | NPY_ITER_ALIGNED | NPY_ITER_READONLY;
// 设置 fancy_dims 的第一个维度为 1,并标记有 fancy 索引存在
mit->fancy_dims[0] = 1;
mit->num_fancy = 1;
}
/*
* 现在有两种一般情况下额外操作(extra_op)的使用方式:
* 1. 不需要子空间迭代,因此额外操作可以包含在索引迭代器中(会被缓冲)。
* 2. 需要子空间迭代,因此额外操作会独立迭代,并且迭代顺序固定为 C 顺序
* (如果数组是 Fortran 顺序,也可以使用 Fortran 顺序)。
* 在这种情况下,子空间迭代器不会被缓冲。
*
* 如果需要子空间迭代,并且给定了额外操作(extra_op),可能还需要对额外操作进行转置
* (或者通知高级迭代器进行转置)。
*/
if (extra_op != NULL) {
/*
* 如果存在额外操作数,需要对其进行准备。
* 1. 子类可能会影响形状,因此需要一个基类。
* 2. 需要确保形状是兼容的。
* 3. 可能需要移除前导的1并转置维度。
* 普通的赋值操作允许广播去除前导的1,但是转置代码不允许这样做。
*/
if (!PyArray_CheckExact(extra_op)) {
// 如果额外操作数不是精确的PyArray对象,将其视图化为PyArray对象
extra_op = (PyArrayObject *)PyArray_View(extra_op, NULL,
&PyArray_Type);
if (extra_op == NULL) {
goto fail;
}
}
else {
// 如果额外操作数已经是精确的PyArray对象,增加其引用计数
Py_INCREF(extra_op);
}
if (PyArray_NDIM(extra_op) > mit->nd) {
/*
* 普通赋值操作允许移除前导的单维度(或等效地添加单维度到被赋值的数组)。
* 为了实现这一点,重新整形数组。
*/
PyArrayObject *tmp_arr;
PyArray_Dims permute;
permute.len = mit->nd;
permute.ptr = &PyArray_DIMS(extra_op)[
PyArray_NDIM(extra_op) - mit->nd];
tmp_arr = (PyArrayObject*)PyArray_Newshape(extra_op, &permute,
NPY_CORDER);
if (tmp_arr == NULL) {
goto broadcast_error;
}
Py_DECREF(extra_op);
extra_op = tmp_arr;
}
/*
* 如果需要前置维度(并且不需要swapaxis),在确保分配额外操作数后使用op_axes。
*/
if (mit->consec) {
PyArray_MapIterSwapAxes(mit, &extra_op, 0);
if (extra_op == NULL) {
goto fail;
}
}
if (subspace && !uses_subspace) {
/*
* 我们没有使用子空间,因此其大小为1。
* 额外操作数对应于子空间的所有维度必须等于1。
*/
if (PyArray_NDIM(subspace) <= PyArray_NDIM(extra_op)) {
j = PyArray_NDIM(subspace);
}
else {
j = PyArray_NDIM(extra_op);
}
for (i = 1; i < j + 1; i++) {
if (PyArray_DIM(extra_op, PyArray_NDIM(extra_op) - i) != 1) {
goto broadcast_error;
}
}
}
}
/*
* 如果子空间不为NULL,NpyIter不能为我们分配额外操作数。
* 这有点笨拙。创建一个虚拟迭代器来找到正确的输出形状和步幅排列。
* TODO: 这可以至少部分地被替换,因为形状已经找到以处理广播错误。
*/
else if (extra_op_flags && (subspace != NULL)) {
npy_uint32 tmp_op_flags[NPY_MAXDIMS];
NpyIter *tmp_iter;
npy_intp stride;
npy_intp strides[NPY_MAXDIMS];
npy_stride_sort_item strideperm[NPY_MAXDIMS];
for (i=0; i < mit->num_fancy; i++) {
tmp_op_flags[i] = NPY_ITER_READONLY;
}
if (PyArray_SIZE(subspace) == 1) {
/* Create an iterator, just to broadcast the arrays?! */
tmp_iter = NpyIter_MultiNew(mit->num_fancy, index_arrays,
NPY_ITER_ZEROSIZE_OK |
NPY_ITER_REFS_OK |
NPY_ITER_MULTI_INDEX |
NPY_ITER_DONT_NEGATE_STRIDES,
NPY_KEEPORDER,
NPY_UNSAFE_CASTING,
tmp_op_flags, NULL);
if (tmp_iter == NULL) {
goto fail;
}
/*
* nditer allows itemsize with npy_intp type, so it works
* here, but it would *not* work directly, since elsize
* is limited to int.
*/
if (!NpyIter_CreateCompatibleStrides(tmp_iter,
extra_op_dtype->elsize * PyArray_SIZE(subspace),
strides)) {
PyErr_SetString(PyExc_ValueError,
"internal error: failed to find output array strides");
goto fail;
}
NpyIter_Deallocate(tmp_iter);
}
else {
/* Just use C-order strides (TODO: allow also F-order) */
stride = extra_op_dtype->elsize * PyArray_SIZE(subspace);
for (i=mit->nd_fancy - 1; i >= 0; i--) {
strides[i] = stride;
stride *= mit->dimensions[i];
}
}
/* shape is set, and strides is set up to mit->nd, set rest */
PyArray_CreateSortedStridePerm(PyArray_NDIM(subspace),
PyArray_STRIDES(subspace), strideperm);
stride = extra_op_dtype->elsize;
for (i=PyArray_NDIM(subspace) - 1; i >= 0; i--) {
strides[mit->nd_fancy + strideperm[i].perm] = stride;
stride *= PyArray_DIM(subspace, (int)strideperm[i].perm);
}
/*
* Allocate new array. Note: Always base class, because
* subclasses might mess with the shape.
*/
Py_INCREF(extra_op_dtype);
extra_op = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type,
extra_op_dtype,
mit->nd_fancy + PyArray_NDIM(subspace),
mit->dimensions, strides,
NULL, 0, NULL);
if (extra_op == NULL) {
goto fail;
}
}
/*
* 如果额外操作 extra_op 存在,则要么已分配,可以由 NpyIter 分配(无子空间),或者根本未使用。
* 需要为 extra_op 设置轴重映射。这将导致忽略子空间维度,并在广播时添加 -1。
*/
if (extra_op) {
for (j=0; j < mit->nd - PyArray_NDIM(extra_op); j++) {
single_op_axis[j] = -1; // 将 -1 添加到轴映射中,以忽略子空间维度
}
for (i=0; i < PyArray_NDIM(extra_op); i++) {
/* (填充子空间维度,但它们不是未使用的)*/
single_op_axis[j++] = i; // 将额外操作的轴映射添加到数组末尾
}
}
/*
* 注意:如果出于某种原因有人希望使用 REDUCE_OK,请小心并修复末尾的错误消息替换。
*/
outer_flags = NPY_ITER_ZEROSIZE_OK |
NPY_ITER_REFS_OK |
NPY_ITER_BUFFERED |
NPY_ITER_DELAY_BUFALLOC |
NPY_ITER_GROWINNER;
/*
* 对于单个一维操作数,保证迭代顺序(scipy 使用这个)。注意子空间可能被使用。
*/
if ((mit->num_fancy == 1) && (PyArray_NDIM(index_arrays[0]) == 1)) {
outer_flags |= NPY_ITER_DONT_NEGATE_STRIDES; // 设置不反转步长的标志
}
/* 如果外部数组被迭代,并且不需要子空间 */
nops = mit->num_fancy; // 设置操作数的数量
if (!uses_subspace) {
outer_flags |= NPY_ITER_EXTERNAL_LOOP; // 设置外部循环标志
}
if (extra_op_flags && !uses_subspace) {
/*
* 注意:这个小限制实际上应该没有关系。
* (替换 npyiter 错误)
*/
if (mit->num_fancy > NPY_MAXDIMS - 1) {
PyErr_Format(PyExc_IndexError,
"when no subspace is given, the number of index "
"arrays cannot be above %d, but %d index arrays found",
NPY_MAXDIMS - 1, mit->num_fancy);
goto fail; // 如果索引数组数量超过限制,触发错误并跳转到失败处理
}
nops += 1; // 增加操作数的数量
index_arrays[mit->num_fancy] = extra_op; // 设置额外操作的索引数组
dtypes[mit->num_fancy] = extra_op_dtype; // 设置额外操作的数据类型
op_flags[mit->num_fancy] = (extra_op_flags |
NPY_ITER_ALLOCATE |
NPY_ITER_NO_SUBTYPE); // 设置额外操作的标志
if (extra_op) {
/* 使用轴重映射 */
op_axes[mit->num_fancy] = single_op_axis; // 设置额外操作的轴映射
mit->outer = NpyIter_AdvancedNew(nops, index_arrays, outer_flags,
NPY_KEEPORDER, NPY_UNSAFE_CASTING, op_flags, dtypes,
mit->nd_fancy, op_axes, mit->dimensions, 0); // 创建高级迭代器
}
else {
mit->outer = NpyIter_MultiNew(nops, index_arrays, outer_flags,
NPY_KEEPORDER, NPY_UNSAFE_CASTING, op_flags, dtypes); // 创建多迭代器
}
}
else {
/* TODO: 可能添加 CORDER 的测试,并且也许允许 F */
mit->outer = NpyIter_MultiNew(nops, index_arrays, outer_flags,
NPY_CORDER, NPY_UNSAFE_CASTING, op_flags, dtypes); // 创建多迭代器
}
/* NpyIter cleanup and information: */
// 如果 dummy_array 存在,则减少 index_arrays[0] 的引用计数
if (dummy_array) {
Py_DECREF(index_arrays[0]);
}
// 如果 mit->outer 为 NULL,则跳转到失败标签
if (mit->outer == NULL) {
goto fail;
}
// 获取 mit->outer 的下一个迭代器函数
mit->outer_next = NpyIter_GetIterNext(mit->outer, NULL);
// 如果获取失败,则跳转到失败标签
if (mit->outer_next == NULL) {
goto fail;
}
// 获取 mit->outer 的数据指针数组
mit->outer_ptrs = NpyIter_GetDataPtrArray(mit->outer);
// 如果不使用子空间,则获取 mit->outer 的内部步幅数组
if (!uses_subspace) {
mit->outer_strides = NpyIter_GetInnerStrideArray(mit->outer);
}
/* Get the allocated extra_op */
// 如果 extra_op_flags 存在
if (extra_op_flags) {
// 如果 extra_op 为 NULL,则将 mit->extra_op 设置为 mit->outer 的操作数数组中的第 mit->num_fancy 个操作数
if (extra_op == NULL) {
mit->extra_op = NpyIter_GetOperandArray(mit->outer)[mit->num_fancy];
}
else {
// 否则,将 mit->extra_op 设置为 extra_op
mit->extra_op = extra_op;
}
// 增加 mit->extra_op 的引用计数
Py_INCREF(mit->extra_op);
}
/*
* If extra_op is being tracked but subspace is used, we need
* to create a dedicated iterator for the outer iteration of
* the extra operand.
*/
// 如果 extra_op_flags 存在并且使用子空间
if (extra_op_flags && uses_subspace) {
// 设置操作轴数组中的第一个元素为 single_op_axis
op_axes[0] = single_op_axis;
// 创建 mit->extra_op_iter 作为 mit->extra_op 的高级新迭代器
mit->extra_op_iter = NpyIter_AdvancedNew(1, &extra_op,
NPY_ITER_ZEROSIZE_OK |
NPY_ITER_REFS_OK |
NPY_ITER_GROWINNER,
NPY_CORDER,
NPY_NO_CASTING,
&extra_op_flags,
NULL,
mit->nd_fancy, op_axes,
mit->dimensions, 0);
// 如果创建失败,则跳转到失败标签
if (mit->extra_op_iter == NULL) {
goto fail;
}
// 获取 mit->extra_op_iter 的下一个迭代器函数
mit->extra_op_next = NpyIter_GetIterNext(mit->extra_op_iter, NULL);
// 如果获取失败,则跳转到失败标签
if (mit->extra_op_next == NULL) {
goto fail;
}
// 获取 mit->extra_op_iter 的数据指针数组
mit->extra_op_ptrs = NpyIter_GetDataPtrArray(mit->extra_op_iter);
}
/* Get the full dimension information */
// 如果 subspace 不为 NULL,则将 mit->baseoffset 设置为 subspace 的字节偏移量
if (subspace != NULL) {
mit->baseoffset = PyArray_BYTES(subspace);
}
else {
// 否则,将 mit->baseoffset 设置为 arr 的字节偏移量
mit->baseoffset = PyArray_BYTES(arr);
}
/* Calculate total size of the MapIter */
// 计算 MapIter 的总大小
mit->size = PyArray_OverflowMultiplyList(mit->dimensions, mit->nd);
// 如果计算结果为负数,则设置异常并跳转到失败标签
if (mit->size < 0) {
PyErr_SetString(PyExc_ValueError,
"advanced indexing operation result is too large");
goto fail;
}
/* Can now return early if no subspace is being used */
// 如果不使用子空间,则减少 extra_op 和 intp_descr 的引用计数,并返回 mit 指针
if (!uses_subspace) {
Py_XDECREF(extra_op);
Py_DECREF(intp_descr);
return (PyObject *)mit;
}
/* Fill in the last bit of mapiter information needed */
/*
* Now just need to create the correct subspace iterator.
*/
// 将 index_arrays[0] 设置为 subspace,并初始化其他相关变量
index_arrays[0] = subspace;
dtypes[0] = NULL;
op_flags[0] = subspace_flags;
op_axes[0] = NULL;
if (extra_op_flags) {
/* 如果存在额外操作标志,表示需要迭代额外操作 */
nops = 2;
index_arrays[1] = extra_op; // 将额外操作数组加入索引数组中
op_axes[1] = &single_op_axis[mit->nd_fancy]; // 设置额外操作的轴
/*
* Buffering is never used here, but in case someone plugs it in
* somewhere else, set the type correctly then.
*/
// 如果启用了缓冲,设置额外操作的数据类型
if ((subspace_iter_flags & NPY_ITER_BUFFERED)) {
dtypes[1] = extra_op_dtype;
}
else {
dtypes[1] = NULL; // 否则将数据类型设为 NULL
}
op_flags[1] = extra_op_flags; // 设置额外操作的操作标志
}
else {
nops = 1; // 如果不存在额外操作,设置操作数为 1
}
// 创建高级迭代器对象,用于处理索引操作
mit->subspace_iter = NpyIter_AdvancedNew(nops, index_arrays,
NPY_ITER_ZEROSIZE_OK |
NPY_ITER_REFS_OK |
NPY_ITER_GROWINNER |
NPY_ITER_EXTERNAL_LOOP |
NPY_ITER_DELAY_BUFALLOC |
subspace_iter_flags,
(nops == 1 ? NPY_CORDER : NPY_KEEPORDER),
NPY_UNSAFE_CASTING,
op_flags, dtypes,
PyArray_NDIM(subspace), op_axes,
&mit->dimensions[mit->nd_fancy], 0);
// 检查迭代器对象是否创建成功
if (mit->subspace_iter == NULL) {
goto fail; // 如果创建失败,跳转到失败处理
}
// 获取迭代器的下一个迭代函数
mit->subspace_next = NpyIter_GetIterNext(mit->subspace_iter, NULL);
// 检查获取下一个迭代函数是否成功
if (mit->subspace_next == NULL) {
goto fail; // 如果获取失败,跳转到失败处理
}
// 获取迭代器的数据指针数组
mit->subspace_ptrs = NpyIter_GetDataPtrArray(mit->subspace_iter);
// 获取迭代器的内部步长数组
mit->subspace_strides = NpyIter_GetInnerStrideArray(mit->subspace_iter);
// 减少额外操作的引用计数
Py_XDECREF(extra_op);
// 减少 intp 描述符的引用计数
Py_DECREF(intp_descr);
// 返回迭代器对象指针,表示成功
return (PyObject *)mit;
fail:
/*
* Check whether the operand could not be broadcast and replace the error
* in that case. This should however normally be found early with a
* direct goto to broadcast_error
*/
// 检查是否无法对操作数进行广播,并在这种情况下替换错误
if (extra_op == NULL) {
goto finish; // 如果额外操作为 NULL,跳转到结束处理
}
// 逆序检查额外操作的维度是否可以广播到索引结果
j = mit->nd;
for (i = PyArray_NDIM(extra_op) - 1; i >= 0; i--) {
j--;
if ((PyArray_DIM(extra_op, i) != 1) &&
/* (j < 0 is currently impossible, extra_op is reshaped) */
j >= 0 &&
PyArray_DIM(extra_op, i) != mit->dimensions[j]) {
/* extra_op cannot be broadcast to the indexing result */
goto broadcast_error; // 如果无法广播,跳转到广播错误处理
}
}
goto finish; // 检查完成,跳转到结束处理
broadcast_error:
/* Report the shape of the original array if it exists */
// 如果存在原始数组,报告其形状
if (original_extra_op == NULL) {
original_extra_op = extra_op; // 如果原始额外操作为 NULL,设置为额外操作
}
// 获取原始额外操作的维度信息
int extra_ndim = PyArray_NDIM(original_extra_op);
npy_intp *extra_dims = PyArray_DIMS(original_extra_op);
// 将维度转换为字符串形式
PyObject *shape1 = convert_shape_to_string(extra_ndim, extra_dims, "");
// 如果转换失败,跳转到结束处理
if (shape1 == NULL) {
goto finish;
}
// 在使用 `mit->consec` 时,反转迭代器形状以进行报告
npy_intp transposed[NPY_MAXDIMS];
// 使用自定义函数获取转置操作后的索引数组,此处mit->nd_fancy可能是用于描述高级索引的数据结构
_get_transpose(mit->nd_fancy, mit->consec, mit->nd, 1, transposed);
// 根据转置后的索引数组,获取相应维度的大小,存储在transposed数组中
for (i = 0; i < mit->nd; i++) {
transposed[i] = mit->dimensions[transposed[i]];
}
// 将转置后的维度信息转换为字符串表示形式,用于错误信息中的格式化输出
PyObject *shape2 = convert_shape_to_string(mit->nd, transposed, "");
if (shape2 == NULL) {
// 如果转换失败,释放shape1并跳转到finish标签处
Py_DECREF(shape1);
goto finish;
}
// 格式化错误信息,指示形状不匹配的问题,包括value array的形状和索引结果的形状
PyErr_Format(PyExc_ValueError,
"shape mismatch: value array of shape %S could not be broadcast "
"to indexing result of shape %S", shape1, shape2);
// 释放shape1和shape2对象
Py_DECREF(shape1);
Py_DECREF(shape2);
finish:
// 释放额外操作对象、整数描述对象和mit对象
Py_XDECREF(extra_op);
Py_DECREF(intp_descr);
Py_DECREF(mit);
// 返回NULL表示函数执行失败
return NULL;
}
NPY_NO_EXPORT PyObject *
PyArray_MapIterArrayCopyIfOverlap(PyArrayObject * a, PyObject * index,
int copy_if_overlap, PyArrayObject *extra_op)
{
PyArrayMapIterObject * mit = NULL;
PyArrayObject *subspace = NULL;
npy_index_info indices[NPY_MAXDIMS * 2 + 1];
int i, index_num, ndim, fancy_ndim, index_type;
PyArrayObject *a_copy = NULL;
index_type = prepare_index(a, index, indices, &index_num,
&ndim, &fancy_ndim, 0);
if (index_type < 0) {
return NULL;
}
if (copy_if_overlap && index_has_memory_overlap(a, index_type, indices,
index_num,
(PyObject *)extra_op)) {
/* Make a copy of the input array */
a_copy = (PyArrayObject *)PyArray_NewLikeArray(a, NPY_ANYORDER,
NULL, 0);
if (a_copy == NULL) {
goto fail;
}
if (PyArray_CopyInto(a_copy, a) != 0) {
goto fail;
}
Py_INCREF(a);
if (PyArray_SetWritebackIfCopyBase(a_copy, a) < 0) {
goto fail;
}
a = a_copy;
}
if (index_type != HAS_FANCY) {
if (get_view_from_index(a, &subspace, indices, index_num, 1) < 0) {
goto fail;
}
}
mit = (PyArrayMapIterObject *)PyArray_MapIterNew(indices, index_num,
index_type, ndim,
fancy_ndim,
a, subspace, 0,
NPY_ITER_READWRITE,
0, NULL, NULL);
if (mit == NULL) {
goto fail;
}
if (PyArray_MapIterCheckIndices(mit) < 0) {
goto fail;
}
if (PyArray_MapIterReset(mit) < 0) {
goto fail;
}
Py_XDECREF(a_copy);
Py_XDECREF(subspace);
for (i=0; i < index_num; i++) {
Py_XDECREF(indices[i].object);
}
return (PyObject *)mit;
fail:
Py_XDECREF(a_copy);
Py_XDECREF(subspace);
Py_XDECREF((PyObject *)mit);
for (i = 0; i < index_num; i++) {
Py_XDECREF(indices[i].object);
}
return NULL;
}
static void
arraymapiter_dealloc(PyArrayMapIterObject *mit)
PyArray_ResolveWritebackIfCopy(mit->array);
Py_XDECREF(mit->array);
Py_XDECREF(mit->subspace);
Py_XDECREF(mit->extra_op);
if (mit->outer != NULL) {
NpyIter_Deallocate(mit->outer);
}
if (mit->subspace_iter != NULL) {
NpyIter_Deallocate(mit->subspace_iter);
}
if (mit->extra_op_iter != NULL) {
NpyIter_Deallocate(mit->extra_op_iter);
}
PyArray_free(mit);
/*
* The mapiter object must be created new each time. It does not work
* to bind to a new array, and continue.
*
* This comment explains that the `mapiter` object in NumPy must be
* instantiated anew for each use. Attempting to rebind it to a new
* array and continue using it does not function correctly.
*/
/*
* This was the original intention, but currently that does not work.
* Do not expose the MapIter_Type to Python.
*
* This comment notes that the original design intention to expose
* `MapIter_Type` to Python has been abandoned due to current
* functionality issues.
*/
/*
* The original mapiter(indexobj); mapiter.bind(a); idea is now fully
* removed. This is not very useful anyway, since mapiter is equivalent
* to a[indexobj].flat but the latter gets to use slice syntax.
*
* This comment clarifies that the concept of using `mapiter(indexobj);
* mapiter.bind(a);` has been completely removed from the implementation.
* It states that using `mapiter` was not particularly useful compared
* to `a[indexobj].flat`, which benefits from more readable slice syntax.
*/
NPY_NO_EXPORT PyTypeObject PyArrayMapIter_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "numpy.mapiter",
.tp_basicsize = sizeof(PyArrayMapIterObject),
.tp_dealloc = (destructor)arraymapiter_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
};