NumPy-源码解析-五十九-

29 阅读53分钟

NumPy 源码解析(五十九)

.\numpy\numpy\_core\src\multiarray\array_coercion.h

#ifndef NUMPY_CORE_SRC_MULTIARRAY_ARRAY_COERCION_H_
#define NUMPY_CORE_SRC_MULTIARRAY_ARRAY_COERCION_H_

/*
 * We do not want to coerce arrays many times unless absolutely necessary.
 * The same goes for sequences, so everything we have seen, we will have
 * to store somehow. This is a linked list of these objects.
 */
// 定义一个结构体,用于缓存强制转换的对象和数组或序列的信息
typedef struct coercion_cache_obj {
    PyObject *converted_obj;    // 转换后的对象
    PyObject *arr_or_sequence;  // 对应的数组或序列对象
    struct coercion_cache_obj *next;  // 指向下一个缓存对象的指针
    npy_bool sequence;  // 表示是否为序列
    int depth;  /* the dimension at which this object was found. */  // 对象发现时的维度
} coercion_cache_obj;

// 将 Python 类型映射为 DType,返回对应的值
NPY_NO_EXPORT int
_PyArray_MapPyTypeToDType(
        PyArray_DTypeMeta *DType, PyTypeObject *pytype, npy_bool userdef);

// 从标量类型的 Python 类型中发现对应的 DType
NPY_NO_EXPORT PyObject *
PyArray_DiscoverDTypeFromScalarType(PyTypeObject *pytype);

// 将原始的标量项从一个描述符类型转换为另一个描述符类型
NPY_NO_EXPORT int
npy_cast_raw_scalar_item(
        PyArray_Descr *from_descr, char *from_item,
        PyArray_Descr *to_descr, char *to_item);

// 将 Python 对象封装为数组描述符对应的值
NPY_NO_EXPORT int
PyArray_Pack(PyArray_Descr *descr, void *item, PyObject *value);

// 根据数组对象和指定的 DTypeMeta,适配数组描述符
NPY_NO_EXPORT PyArray_Descr *
PyArray_AdaptDescriptorToArray(
        PyArrayObject *arr, PyArray_DTypeMeta *dtype, PyArray_Descr *descr);

// 从 Python 对象中发现数组的 DType 和形状
NPY_NO_EXPORT int
PyArray_DiscoverDTypeAndShape(
        PyObject *obj, int max_dims,
        npy_intp out_shape[NPY_MAXDIMS],
        coercion_cache_obj **coercion_cache,
        PyArray_DTypeMeta *fixed_DType, PyArray_Descr *requested_descr,
        PyArray_Descr **out_descr, int copy, int *was_copied_by__array__);

// 发现数组参数并返回相应的 Python 对象
NPY_NO_EXPORT PyObject *
_discover_array_parameters(PyObject *NPY_UNUSED(self),
        PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames);

// 递归释放 coercion_cache_obj 结构体及其子对象
// 释放 coercion_cache_obj 结构体的递归释放函数
NPY_NO_EXPORT void
npy_free_coercion_cache(coercion_cache_obj *first);

// 断开单个缓存项并返回下一个缓存项
NPY_NO_EXPORT coercion_cache_obj *
npy_unlink_coercion_cache(coercion_cache_obj *current);

// 从缓存中将数据分配到数组对象中
NPY_NO_EXPORT int
PyArray_AssignFromCache(PyArrayObject *self, coercion_cache_obj *cache);

#endif  /* NUMPY_CORE_SRC_MULTIARRAY_ARRAY_COERCION_H_ */

.\numpy\numpy\_core\src\multiarray\array_converter.c

/*
 * This file defines an _array_converter object used internally in NumPy to
 * deal with `__array_wrap__` and `result_type()` for multiple arguments
 * where converting inputs to arrays would lose the necessary information.
 *
 * The helper thus replaces many asanyarray/asarray calls.
 */

#define NPY_NO_DEPRECATED_API NPY_API_VERSION  // Define to prevent use of deprecated API
#define _MULTIARRAYMODULE  // Define to indicate this is part of the multiarray module

#define PY_SSIZE_T_CLEAN  // Define to use the cleaned-up Py_ssize_t API
#include <Python.h>  // Include Python.h for Python/C API
#include <structmember.h>  // Include structmember.h for structure member definitions

#include "numpy/arrayobject.h"  // Include arrayobject.h for NumPy array object definitions
#include "arrayobject.h"  // Include arrayobject.h for array-related functions
#include "array_converter.h"  // Include array_converter.h for array conversion utilities
#include "arraywrap.h"  // Include arraywrap.h for array wrapping functions
#include "numpy/arrayscalars.h"  // Include arrayscalars.h for NumPy array scalars
#include "npy_argparse.h"  // Include npy_argparse.h for argument parsing utilities
#include "abstractdtypes.h"  // Include abstractdtypes.h for abstract data type definitions
#include "convert_datatype.h"  // Include convert_datatype.h for data type conversion utilities
#include "descriptor.h"  // Include descriptor.h for descriptor definitions
#include "npy_static_data.h"  // Include npy_static_data.h for static data used in NumPy
#include "ctors.h"  // Include ctors.h for constructor functions

#include "npy_config.h"  // Include npy_config.h for NumPy configuration

#include "array_assign.h"  // Include array_assign.h for array assignment utilities

#include "common.h"  // Include common.h for common utility functions
#include "get_attr_string.h"  // Include get_attr_string.h for functions dealing with attribute strings

// Define a static function to create a new array converter object
static PyObject *
array_converter_new(
        PyTypeObject *cls, PyObject *args, PyObject *kwds)
{
    // Check if keywords are provided; array creation helper doesn't support keywords
    if (kwds != NULL && PyDict_GET_SIZE(kwds) != 0) {
        PyErr_SetString(PyExc_TypeError,
                "Array creation helper doesn't support keywords.");
        return NULL;
    }

    // Determine the number of arguments passed
    Py_ssize_t narrs_ssize_t = (args == NULL) ? 0 : PyTuple_GET_SIZE(args);
    int narrs = (int)narrs_ssize_t;

    // Limit the number of arguments to NPY_MAXARGS
    /* Limit to NPY_MAXARGS for now. */
    if (narrs_ssize_t > NPY_MAXARGS) {
        PyErr_SetString(PyExc_RuntimeError,
            "too many arrays.");
        return NULL;
    }

    // Allocate memory for the PyArrayArrayConverterObject instance
    PyArrayArrayConverterObject *self = PyObject_NewVar(
            PyArrayArrayConverterObject, cls, narrs);
    if (self == NULL) {
        return NULL;
    }

    // Initialize the PyObject instance with PyArrayArrayConverter_Type and narrs
    PyObject_InitVar((PyVarObject *)self, &PyArrayArrayConverter_Type, narrs);

    // Initialize attributes of the array converter object
    self->narrs = 0;
    self->flags = 0;
    self->wrap = NULL;
    self->wrap_type = NULL;

    // If no arguments are passed, return the initialized object
    if (narrs == 0) {
        return (PyObject *)self;
    }

    // Set flags for the array converter object
    self->flags = (NPY_CH_ALL_PYSCALARS | NPY_CH_ALL_SCALARS);

    // Initialize creation_item pointer for iterating over items
    creation_item *item = self->items;

    // Increase self->narrs in loop for cleanup
    /* increase self->narrs in loop for cleanup */
    for (int i = 0; i < narrs; i++, item++) {
        // 将参数元组中的第 i 个对象赋给当前处理的 item 的 object 字段
        item->object = PyTuple_GET_ITEM(args, i);

        /* Fast path if input is an array (maybe FromAny should be faster): */
        // 如果 item->object 是一个 NumPy 数组,则执行快速路径
        if (PyArray_Check(item->object)) {
            // 增加 item->object 的引用计数
            Py_INCREF(item->object);
            // 将 item->object 转换为 PyArrayObject,并赋给 item->array
            item->array = (PyArrayObject *)item->object;
            // 表示 item->object 不是标量输入
            item->scalar_input = 0;
        }
        else {
            // 从 item->object 创建 PyArrayObject 对象,尝试转换为 NumPy 数组
            item->array = (PyArrayObject *)PyArray_FromAny_int(
                    item->object, NULL, NULL, 0, 0, 0, NULL,
                    &item->scalar_input);
            // 如果转换失败,跳转到失败标签 fail
            if (item->array == NULL) {
                goto fail;
            }
        }

        /* At this point, assume cleanup should happen for this item */
        // 假设此时应为该 item 执行清理工作
        self->narrs++;
        // 增加 item->object 的引用计数
        Py_INCREF(item->object);
        // 获取 item->array 的数据类型,并赋给 item->DType
        item->DType = NPY_DTYPE(PyArray_DESCR(item->array));
        // 增加 item->DType 的引用计数
        Py_INCREF(item->DType);

        /*
         * Check whether we were passed a an int/float/complex Python scalar.
         * If not, set `descr` and clear pyscalar/scalar flags as needed.
         */
        // 检查是否传递了 int/float/complex 类型的 Python 标量
        if (item->scalar_input && npy_mark_tmp_array_if_pyscalar(
                item->object, item->array, &item->DType)) {
            // 如果是 Python 标量,则设置 item->descr 为 NULL
            item->descr = NULL;
            // 不标记存储的数组为 Python 文字量
            ((PyArrayObject_fields *)(item->array))->flags &= (
                    ~NPY_ARRAY_WAS_PYTHON_LITERAL);
        }
        else {
            // 否则获取 item->array 的描述符,并增加引用计数
            item->descr = PyArray_DESCR(item->array);
            Py_INCREF(item->descr);

            // 如果不是标量输入
            if (item->scalar_input) {
                // 清除 self 的标量输入标志位
                self->flags &= ~NPY_CH_ALL_PYSCALARS;
            }
            else {
                // 清除 self 的标量输入和标量标志位
                self->flags &= ~(NPY_CH_ALL_PYSCALARS | NPY_CH_ALL_SCALARS);
            }
        }
    }

    // 成功处理完所有项后,返回 self 对象作为 PyObject 指针
    return (PyObject *)self;

  fail:
    // 处理失败时,减少 self 的引用计数并返回 NULL
    Py_DECREF(self);
    return NULL;
static PyObject *
array_converter_get_scalar_input(PyArrayArrayConverterObject *self)
{
    // 创建一个元组,用于存放返回结果,元组长度为 self->narrs
    PyObject *ret = PyTuple_New(self->narrs);
    if (ret == NULL) {
        return NULL;
    }

    // 遍历 self->items 数组,为每个元素创建一个布尔值对象,表示是否为标量输入
    creation_item *item = self->items;
    for (int i = 0; i < self->narrs; i++, item++) {
        if (item->scalar_input) {
            // 如果是标量输入,增加 True 的引用计数,并设置到元组的第 i 个位置
            Py_INCREF(Py_True);
            PyTuple_SET_ITEM(ret, i, Py_True);
        }
        else {
            // 如果不是标量输入,增加 False 的引用计数,并设置到元组的第 i 个位置
            Py_INCREF(Py_False);
            PyTuple_SET_ITEM(ret, i, Py_False);
        }
    }
    return ret;  // 返回填充好的元组对象
}

static int
find_wrap(PyArrayArrayConverterObject *self)
{
    // 如果 self->wrap 已经被设置,直接返回 0,不执行任何操作
    if (self->wrap != NULL) {
        return 0;  /* nothing to do */
    }

    // 分配临时空间用于存放对象指针,长度为 self->narrs
    PyObject **objects = PyMem_Malloc(self->narrs * sizeof(PyObject *));
    if (objects == NULL) {
        PyErr_NoMemory();  // 内存分配失败,设置内存错误并返回 -1
        return -1;
    }

    // 将 self->items 数组中的 object 指针复制到 objects 数组中
    for (int i = 0; i < self->narrs; i++) {
        objects[i] = self->items[i].object;
    }

    // 调用 npy_find_array_wrap 函数查找数组包装器,将结果存储在 self->wrap 和 self->wrap_type 中
    int ret = npy_find_array_wrap(
            self->narrs, objects, &self->wrap, &self->wrap_type);

    PyMem_FREE(objects);  // 释放临时对象数组的内存空间
    return ret;  // 返回 npy_find_array_wrap 函数的执行结果
}

typedef enum {
    CONVERT = 0,
    PRESERVE = 1,
    CONVERT_IF_NO_ARRAY = 2,
} scalar_policy;

static int
pyscalar_mode_conv(PyObject *obj, scalar_policy *policy)
{
    // 预先定义三个字符串对象的数组
    PyObject *strings[3] = {
            npy_interned_str.convert, npy_interned_str.preserve,
            npy_interned_str.convert_if_no_array};

    // 第一轮快速匹配,通过对象的身份进行比较
    for (int i = 0; i < 3; i++) {
        if (obj == strings[i]) {
            *policy = i;  // 匹配成功,设置 policy 的值为 i
            return 1;     // 返回匹配成功的标志
        }
    }

    // 第二轮比较,通过 PyObject_RichCompareBool 函数进行比较
    for (int i = 0; i < 3; i++) {
        int cmp = PyObject_RichCompareBool(obj, strings[i], Py_EQ);
        if (cmp < 0) {
            return 0;  // 比较失败,返回错误标志
        }
        if (cmp) {
            *policy = i;  // 匹配成功,设置 policy 的值为 i
            return 1;     // 返回匹配成功的标志
        }
    }

    // 如果没有匹配成功,则设置 ValueError 异常并返回错误标志
    PyErr_SetString(PyExc_ValueError,
            "invalid pyscalar mode, must be 'convert', 'preserve', or "
            "'convert_if_no_array' (default).");
    return 0;  // 返回匹配失败的标志
}

static PyObject *
array_converter_as_arrays(PyArrayArrayConverterObject *self,
        PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
    // 设置默认的 subok 和 policy 值
    npy_bool subok = NPY_TRUE;
    scalar_policy policy = CONVERT_IF_NO_ARRAY;

    // 解析函数参数,其中 "as_arrays" 是函数名
    NPY_PREPARE_ARGPARSER;
    if (npy_parse_arguments("as_arrays", args, len_args, kwnames,
            "$subok", &PyArray_BoolConverter, &subok,
            /* how to handle scalars (ignored if dtype is given). */
            "$pyscalars", &pyscalar_mode_conv, &policy,
            NULL, NULL, NULL) < 0) {
        return NULL;  // 解析参数失败,返回 NULL
    }

    // 如果 policy 为 CONVERT_IF_NO_ARRAY,则根据 self->flags 的设置调整 policy 的值
    if (policy == CONVERT_IF_NO_ARRAY) {
        if (self->flags & NPY_CH_ALL_PYSCALARS) {
            policy = CONVERT;
        }
        else {
            policy = PRESERVE;
        }
    }

    // 创建一个元组用于存放返回结果,长度为 self->narrs
    PyObject *res = PyTuple_New(self->narrs);
    if (res == NULL) {
        return NULL;  // 创建元组失败,返回 NULL
    }

    // 获取 self->items 数组的首地址
    creation_item *item = self->items;
    // 继续函数实现的下一步操作...
    // 循环遍历 self 对象的 narrs 个数
    for (int i = 0; i < self->narrs; i++, item++) {
        // 定义结果项指针 res_item
        PyObject *res_item;
        // 如果 item 的描述符为 NULL 并且策略为 PRESERVE
        if (item->descr == NULL && policy == PRESERVE) {
            // 将 res_item 指向 item 的对象,并增加其引用计数
            res_item = item->object;
            Py_INCREF(res_item);
        }
        // 否则
        else {
            // 将 res_item 指向 item 的数组对象,并增加其引用计数
            res_item = (PyObject *)item->array;
            Py_INCREF(res_item);
            // 如果 subok 参数为假
            if (!subok) {
                /* PyArray_EnsureArray steals the reference... */
                // 调用 PyArray_EnsureArray 确保 res_item 是数组对象
                res_item = PyArray_EnsureArray(res_item);
                // 如果返回值为 NULL,则跳转到 fail 标签
                if (res_item == NULL) {
                    goto fail;
                }
            }
        }

        // 将 res_item 添加到元组 res 的第 i 个位置
        if (PyTuple_SetItem(res, i, res_item) < 0) {
            // 如果添加失败,则跳转到 fail 标签
            goto fail;
        }
    }

    // 成功返回结果元组 res
    return res;

  fail:
    // 出错时,减少结果元组 res 的引用计数
    Py_DECREF(res);
    // 返回 NULL 指针
    return NULL;
}


static PyObject *
array_converter_wrap(PyArrayArrayConverterObject *self,
        PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
    PyObject *obj;
    PyObject *to_scalar = Py_None;
    npy_bool ensure_scalar;

    // 检查是否已经初始化包装器,若未初始化则返回 NULL
    if (find_wrap(self) < 0) {
        return NULL;
    }

    NPY_PREPARE_ARGPARSER;
    // 解析参数,支持额外参数 $to_scalar 作为布尔值,若为 None 则由输入对象决定
    if (npy_parse_arguments("wrap", args, len_args, kwnames,
            "", NULL, &obj,
            /* Three-way "bool", if `None` inspect input to decide. */
            "$to_scalar", NULL, &to_scalar,
            NULL, NULL, NULL) < 0) {
        return NULL;
    }
    // 根据 $to_scalar 决定是否确保对象为标量
    if (to_scalar == Py_None) {
        ensure_scalar = self->flags & NPY_CH_ALL_SCALARS;
    }
    else {
        // 将 $to_scalar 转换为布尔值,若转换失败则返回 NULL
        if (!PyArray_BoolConverter(to_scalar, &ensure_scalar)) {
            return NULL;
        }
    }

    // 应用包装操作并返回结果
    return npy_apply_wrap(
        obj, NULL, self->wrap, self->wrap_type, NULL, ensure_scalar, NPY_FALSE);
}


static PyObject *
array_converter_result_type(PyArrayArrayConverterObject *self,
        PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
    PyArray_Descr *result = NULL;
    npy_dtype_info dt_info = {NULL, NULL};
    npy_bool ensure_inexact = NPY_FALSE;

    /* 分配临时空间(可以优化掉) */
    void *DTypes_and_descrs = PyMem_Malloc(
            ((self->narrs + 1) * 2) * sizeof(PyObject *));
    if (DTypes_and_descrs == NULL) {
        PyErr_NoMemory();
        return NULL;
    }
    PyArray_DTypeMeta **DTypes = DTypes_and_descrs;
    PyArray_Descr **descrs = (PyArray_Descr **)(DTypes + self->narrs + 1);

    NPY_PREPARE_ARGPARSER;
    // 解析参数,支持可选参数 "extra_dtype" 和 "ensure_inexact"
    if (npy_parse_arguments("result_type", args, len_args, kwnames,
            "|extra_dtype", &PyArray_DTypeOrDescrConverterOptional, &dt_info,
            "|ensure_inexact", &PyArray_BoolConverter, &ensure_inexact,
            NULL, NULL, NULL) < 0) {
        goto finish;
    }

    int ndescrs = 0;
    int nDTypes = 0;
    creation_item *item = self->items;
    // 遍历项目列表,收集数据类型和描述符
    for (int i = 0; i < self->narrs; i++, item++) {
        DTypes[nDTypes] = item->DType;
        nDTypes++;
        if (item->descr != NULL) {
            descrs[ndescrs] = item->descr;
            ndescrs++;
        }
    }

    // 若需要确保结果为非精确数,则设置默认浮点数类型
    if (ensure_inexact) {
        if (dt_info.dtype != NULL) {
            PyErr_SetString(PyExc_TypeError,
                    "extra_dtype and ensure_inexact are mutually exclusive.");
            goto finish;
        }
        Py_INCREF(&PyArray_PyFloatDType);
        dt_info.dtype = &PyArray_PyFloatDType;
    }

    // 添加额外的数据类型和描述符
    if (dt_info.dtype != NULL) {
        DTypes[nDTypes] = dt_info.dtype;
        nDTypes++;
    }
    if (dt_info.descr != NULL) {
        descrs[ndescrs] = dt_info.descr;
        ndescrs++;
    }

    // 推断通用数据类型
    PyArray_DTypeMeta *common_dtype = PyArray_PromoteDTypeSequence(
            nDTypes, DTypes);
    if (common_dtype == NULL) {
        goto finish;
    }
    // 若无描述符,则调用默认描述符生成函数
    if (ndescrs == 0) {
        result = NPY_DT_CALL_default_descr(common_dtype);
    }
    else {
        // 否则,调用 PyArray_CastToDTypeAndPromoteDescriptors 函数进行类型转换和描述符提升
        result = PyArray_CastToDTypeAndPromoteDescriptors(
                ndescrs, descrs, common_dtype);
    }
    // 释放 common_dtype 对象的引用计数
    Py_DECREF(common_dtype);

  finish:
    // 释放 dt_info.descr 对象的引用计数
    Py_XDECREF(dt_info.descr);
    // 释放 dt_info.dtype 对象的引用计数
    Py_XDECREF(dt_info.dtype);
    // 释放 DTypes_and_descrs 所占用的内存
    PyMem_Free(DTypes_and_descrs);
    // 返回 result 对象作为 PyObject 指针类型的结果
    return (PyObject *)result;
# 定义一个静态的属性获取器和设置器数组,用于Python对象的特性获取和设置
static PyGetSetDef array_converter_getsets[] = {
    {"scalar_input",
        (getter)array_converter_get_scalar_input,  # 获取标量输入的函数指针
        NULL,  # 没有设置器,因此设为NULL
        NULL, NULL},  # 获取器和设置器的文档字符串为空
    {NULL, NULL, NULL, NULL, NULL},  # 结束符号,用于指示属性列表的结束
};

# 定义一个静态的方法数组,用于Python对象的方法定义
static PyMethodDef array_converter_methods[] = {
    {"as_arrays", 
        (PyCFunction)array_converter_as_arrays,  # 指向转换为数组的函数指针
        METH_FASTCALL | METH_KEYWORDS, NULL},  # 使用快速调用和关键字参数的方法标志,文档字符串为空
    {"result_type",
        (PyCFunction)array_converter_result_type,  # 指向结果类型函数的函数指针
        METH_FASTCALL | METH_KEYWORDS, NULL},  # 使用快速调用和关键字参数的方法标志,文档字符串为空
    {"wrap",
        (PyCFunction)array_converter_wrap,  # 指向包装函数的函数指针
        METH_FASTCALL | METH_KEYWORDS, NULL},  # 使用快速调用和关键字参数的方法标志,文档字符串为空
    {NULL, NULL, 0, NULL}  # 结束符号,用于指示方法列表的结束
};

# 定义一个静态的析构函数,用于释放内存和资源
static void
array_converter_dealloc(PyArrayArrayConverterObject *self)
{
    creation_item *item = self->items;  # 初始化创建项指针,指向self对象的items数组
    for (int i = 0; i < self->narrs; i++, item++) {
        Py_XDECREF(item->array);  # 递减引用计数并释放数组对象
        Py_XDECREF(item->object);  # 递减引用计数并释放Python对象
        Py_XDECREF(item->DType);  # 递减引用计数并释放数据类型对象
        Py_XDECREF(item->descr);  # 递减引用计数并释放描述符对象
    }

    Py_XDECREF(self->wrap);  # 递减引用计数并释放wrap对象
    Py_XDECREF(self->wrap_type);  # 递减引用计数并释放wrap_type对象
    PyObject_Del((PyObject *)self);  # 删除Python对象self并释放其内存
}

# 定义一个长度函数,返回PyArrayArrayConverterObject对象的长度
static Py_ssize_t
array_converter_length(PyArrayArrayConverterObject *self)
{
    return self->narrs;  # 返回对象self的narrs属性,即数组的数量
}

# 定义一个项目获取函数,返回PyArrayArrayConverterObject对象的第item项
static PyObject *
array_converter_item(PyArrayArrayConverterObject *self, Py_ssize_t item)
{
    /* Python ensures no negative indices (and probably the below also) */
    # Python确保没有负索引(也可能下面的操作也是如此)

    if (item < 0 || item >= self->narrs) {
        PyErr_SetString(PyExc_IndexError, "invalid index");  # 设置索引错误异常并返回NULL
        return NULL;
    }

    /* Follow the `as_arrays` default of `CONVERT_IF_NO_ARRAY`: */
    # 遵循`as_arrays`的默认设置`CONVERT_IF_NO_ARRAY`

    PyObject *res;
    if (self->items[item].descr == NULL
            && !(self->flags & NPY_CH_ALL_PYSCALARS)) {
        res = self->items[item].object;  # 如果描述符为NULL且标志中不包含所有Python标量,则结果为对象
    }
    else {
        res = (PyObject *)self->items[item].array;  # 否则结果为数组对象的PyObject指针转换
    }

    Py_INCREF(res);  # 增加结果对象的引用计数
    return res;  # 返回结果对象
}

# 定义一个序列方法结构体,包含了长度函数和项目获取函数
static PySequenceMethods array_converter_as_sequence = {
    .sq_length = (lenfunc)array_converter_length,  # 序列长度函数指针
    .sq_item = (ssizeargfunc)array_converter_item,  # 序列项目获取函数指针
};

# 定义一个PyTypeObject对象,描述PyArrayArrayConverterObject对象的类型信息
NPY_NO_EXPORT PyTypeObject PyArrayArrayConverter_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)  # 初始化Python对象头部信息
    .tp_name = "numpy._core._multiarray_umath._array_converter",  # 类型对象的名称
    .tp_basicsize = sizeof(PyArrayArrayConverterObject),  # 类型对象的基本大小
    .tp_itemsize = sizeof(creation_item),  # 类型对象的每个项的大小
    .tp_new = array_converter_new,  # 类型对象的构造函数
    .tp_dealloc = (destructor)array_converter_dealloc,  # 类型对象的析构函数
    .tp_flags = Py_TPFLAGS_DEFAULT,  # 类型对象的标志
    .tp_getset = array_converter_getsets,  # 类型对象的属性获取器和设置器数组
    .tp_methods = array_converter_methods,  # 类型对象的方法数组
    .tp_as_sequence = &array_converter_as_sequence,  # 类型对象的序列方法结构体
};

.\numpy\numpy\_core\src\multiarray\array_converter.h

#ifndef NUMPY_CORE_SRC_MULTIARRAY_ARRAY_CONVERTER_H_
// 如果未定义 NUMPY_CORE_SRC_MULTIARRAY_ARRAY_CONVERTER_H_ 宏,则开始条件编译
#define NUMPY_CORE_SRC_MULTIARRAY_ARRAY_CONVERTER_H_

// 包含 ndarraytypes.h 文件,定义了与 ndarray 相关的类型和宏
#include "numpy/ndarraytypes.h"

// 声明 PyArrayArrayConverter_Type 变量,不导出给外部
extern NPY_NO_EXPORT PyTypeObject PyArrayArrayConverter_Type;

// 定义枚举类型 npy_array_converter_flags,用于标志数组转换器的特性
typedef enum {
    NPY_CH_ALL_SCALARS = 1 << 0,         // 表示所有对象都是标量
    NPY_CH_ALL_PYSCALARS = 1 << 1,       // 表示所有对象都是 Python 标量对象
} npy_array_converter_flags;

// 定义结构体 creation_item,描述创建对象的元数据
typedef struct {
    PyObject *object;                    // Python 对象
    PyArrayObject *array;                // NumPy 数组对象
    PyArray_DTypeMeta *DType;            // NumPy 数据类型元信息
    PyArray_Descr *descr;                // NumPy 数据描述符
    int scalar_input;                    // 是否标量输入的标志
} creation_item;

// 定义结构体 PyArrayArrayConverterObject,表示数组转换器对象
typedef struct {
    PyObject_VAR_HEAD                     // 可变大小对象的头部
    int narrs;                            // 数组的数量
    npy_array_converter_flags flags;      // 数组转换器的特性标志
    PyObject *wrap;                       // __array_wrap__ 缓存对象
    PyObject *wrap_type;                  // __array_wrap__ 方法的类型
    creation_item items[];                // 创建对象的元数据数组
} PyArrayArrayConverterObject;

#endif  /* NUMPY_CORE_SRC_MULTIARRAY_ARRAY_CONVERTER_H_ */

.\numpy\numpy\_core\src\multiarray\array_method.c

/*
 * This file implements an abstraction layer for "Array methods", which
 * work with a specific DType class input and provide low-level C function
 * pointers to do fast operations on the given input functions.
 * It thus adds an abstraction layer around individual ufunc loops.
 *
 * Unlike methods, a ArrayMethod can have multiple inputs and outputs.
 * This has some serious implication for garbage collection, and as far
 * as I (@seberg) understands, it is not possible to always guarantee correct
 * cyclic garbage collection of dynamically created DTypes with methods.
 * The keyword (or rather the solution) for this seems to be an "ephemeron"
 * which I believe should allow correct garbage collection but seems
 * not implemented in Python at this time.
 * The vast majority of use-cases will not require correct garbage collection.
 * Some use cases may require the user to be careful.
 *
 * Generally there are two main ways to solve this issue:
 *
 * 1. A method with a single input (or inputs of all the same DTypes) can
 *    be "owned" by that DType (it becomes unusable when the DType is deleted).
 *    This holds especially for all casts, which must have a defined output
 *    DType and must hold on to it strongly.
 * 2. A method which can infer the output DType(s) from the input types does
 *    not need to keep the output type alive. (It can use NULL for the type,
 *    or an abstract base class which is known to be persistent.)
 *    It is then sufficient for a ufunc (or other owner) to only hold a
 *    weak reference to the input DTypes.
 */
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _UMATHMODULE
#define _MULTIARRAYMODULE

#include <npy_pycompat.h>
#include "arrayobject.h"
#include "array_coercion.h"
#include "array_method.h"
#include "dtypemeta.h"
#include "convert_datatype.h"
#include "common.h"
#include "numpy/ufuncobject.h"


/*
 * The default descriptor resolution function.  The logic is as follows:
 *
 * 1. The output is ensured to be canonical (currently native byte order),
 *    if it is of the correct DType.
 * 2. If any DType is was not defined, it is replaced by the common DType
 *    of all inputs. (If that common DType is parametric, this is an error.)
 *
 * We could allow setting the output descriptors specifically to simplify
 * this step.
 *
 * Note that the default version will indicate that the cast can be done
 * as using `arr.view(new_dtype)` if the default cast-safety is
 * set to "no-cast".  This default function cannot be used if a view may
 * be sufficient for casting but the cast is not always "no-cast".
 */
static NPY_CASTING
default_resolve_descriptors(
        PyArrayMethodObject *method,
        PyArray_DTypeMeta *const *dtypes,
        PyArray_Descr *const *input_descrs,
        PyArray_Descr **output_descrs,
        npy_intp *view_offset)
{
    int nin = method->nin;     // Number of input arguments for the method
    int nout = method->nout;   // Number of output arguments for the method
    // 遍历输入输出数组的所有元素
    for (int i = 0; i < nin + nout; i++) {
        // 获取当前元素对应的数据类型
        PyArray_DTypeMeta *dtype = dtypes[i];
        // 如果输入描述符不为空,将输出描述符设为规范化后的输入描述符
        if (input_descrs[i] != NULL) {
            output_descrs[i] = NPY_DT_CALL_ensure_canonical(input_descrs[i]);
        }
        // 否则,使用默认数据类型描述符
        else {
            output_descrs[i] = NPY_DT_CALL_default_descr(dtype);
        }
        // 如果输出描述符为空,跳转到失败处理标签
        if (NPY_UNLIKELY(output_descrs[i] == NULL)) {
            goto fail;
        }
    }
    /*
     * 如果方法的类型转换设置为 NPY_NO_CASTING,
     * 则假设所有的类型转换都不需要,视为可视的情况下。
     */
    if (method->casting == NPY_NO_CASTING) {
        /*
         * 根据当前定义,无类型转换应该意味着可视化。
         * 例如,对象到对象的转换就会标记为可视化。
         */
        *view_offset = 0;
    }
    // 返回当前方法的类型转换设置
    return method->casting;

  fail:
    // 失败时释放已分配的输出描述符内存
    for (int i = 0; i < nin + nout; i++) {
        Py_XDECREF(output_descrs[i]);
    }
    // 返回失败状态
    return -1;
/**
 * Check if the given strides match the element sizes of the descriptors,
 * indicating contiguous memory layout for each argument.
 *
 * @param strides An array of strides for each argument.
 * @param descriptors Array of descriptors for each argument.
 * @param nargs Number of arguments (descriptors and strides).
 * @return 1 if all strides match descriptors' element sizes, otherwise 0.
 */
static inline int
is_contiguous(
        npy_intp const *strides, PyArray_Descr *const *descriptors, int nargs)
{
    // Iterate through each argument
    for (int i = 0; i < nargs; i++) {
        // Check if the stride of the argument matches its descriptor's element size
        if (strides[i] != descriptors[i]->elsize) {
            return 0; // Not contiguous if stride doesn't match element size
        }
    }
    return 1; // All strides match descriptors' element sizes, indicating contiguous memory
}


/**
 * The default method to fetch the correct loop for a cast or ufunc
 * (at the time of writing only casts).
 * Note that the default function provided here will only indicate that a cast
 * can be done as a view (i.e., arr.view(new_dtype)) when this is trivially
 * true, i.e., for cast safety "no-cast". It will not recognize view as an
 * option for other casts (e.g., viewing '>i8' as '>i4' with an offset of 4).
 *
 * @param context The context object containing method descriptors and flags.
 * @param aligned Flag indicating if memory should be aligned.
 * @param move_references UNUSED (currently not used in the function).
 * @param strides An array of strides for each argument.
 * @param descriptors Array of descriptors for each argument.
 * @param out_loop Pointer to store the selected strided loop.
 * @param out_transferdata Unused in this function, set to NULL.
 * @param flags Pointer to store method flags.
 * @return 0 on success, -1 on failure.
 */
NPY_NO_EXPORT int
npy_default_get_strided_loop(
        PyArrayMethod_Context *context,
        int aligned, int NPY_UNUSED(move_references), const npy_intp *strides,
        PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata,
        NPY_ARRAYMETHOD_FLAGS *flags)
{
    PyArray_Descr *const *descrs = context->descriptors; // Array of descriptors
    PyArrayMethodObject *meth = context->method; // Method object from context
    *flags = meth->flags & NPY_METH_RUNTIME_FLAGS; // Set method flags

    int nargs = meth->nin + meth->nout; // Total number of arguments

    // Determine which loop to select based on alignment
    if (aligned) {
        // Use contiguous loop if available and if arguments are contiguous
        if (meth->contiguous_loop == NULL ||
                !is_contiguous(strides, descrs, nargs)) {
            *out_loop = meth->strided_loop;
            return 0;
        }
        *out_loop = meth->contiguous_loop;
    }
    else {
        // Use unaligned strided loop if available and if arguments are contiguous
        if (meth->unaligned_contiguous_loop == NULL ||
                !is_contiguous(strides, descrs, nargs)) {
            *out_loop = meth->unaligned_strided_loop;
            return 0;
        }
        *out_loop = meth->unaligned_contiguous_loop;
    }
    return 0; // Success
}


/**
 * Validate that the input specification is usable to create a new ArrayMethod.
 *
 * @param spec The specification object to validate.
 * @return 0 on success, -1 on error.
 */
static int
validate_spec(PyArrayMethod_Spec *spec)
{
    int nargs = spec->nin + spec->nout; // Total number of arguments

    // Check for invalid input specification fields/values
    if (spec->nin < 0 || spec->nout < 0 || nargs > NPY_MAXARGS) {
        PyErr_Format(PyExc_ValueError,
                "ArrayMethod inputs and outputs must be greater zero and"
                "not exceed %d. (method: %s)", NPY_MAXARGS, spec->name);
        return -1; // Return error if inputs/outputs are invalid
    }

    // Check for valid casting options
    switch (spec->casting) {
        case NPY_NO_CASTING:
        case NPY_EQUIV_CASTING:
        case NPY_SAFE_CASTING:
        case NPY_SAME_KIND_CASTING:
        case NPY_UNSAFE_CASTING:
            break; // Valid casting types
        default:
            if (spec->casting != -1) {
                PyErr_Format(PyExc_TypeError,
                        "ArrayMethod has invalid casting `%d`. (method: %s)",
                        spec->casting, spec->name);
                return -1; // Return error for invalid casting type
            }
    }

    // Input specification is valid
    return 0;
}
    for (int i = 0; i < nargs; i++) {
        /*
         * 注意,我们可以允许输出数据类型未指定
         * (数组方法必须确保支持此功能)。
         * 我们甚至可以允许某些数据类型是抽象的。
         * 目前,假设这最好在提升步骤中处理。
         * 提供所有数据类型的一个问题是明确需要保持引用。
         * 我们可能最终需要实现遍历并信任 GC 处理它。
         */

        // 检查是否存在未指定的输出数据类型
        if (spec->dtypes[i] == NULL) {
            // 报错:ArrayMethod 必须提供所有输入和输出的数据类型。
            PyErr_Format(PyExc_TypeError,
                    "ArrayMethod must provide all input and output DTypes. "
                    "(method: %s)", spec->name);
            return -1;
        }

        // 检查提供的数据类型对象是否为 DType 类型
        if (!PyObject_TypeCheck(spec->dtypes[i], &PyArrayDTypeMeta_Type)) {
            // 报错:提供的对象不是 DType 类型。
            PyErr_Format(PyExc_TypeError,
                    "ArrayMethod provided object %R is not a DType."
                    "(method: %s)", spec->dtypes[i], spec->name);
            return -1;
        }
    }
    // 所有检查通过,返回成功状态
    return 0;
/**
 * Initialize a new BoundArrayMethodObject from slots.  Slots which are
 * not provided may be filled with defaults.
 *
 * @param res The new PyBoundArrayMethodObject to be filled.
 * @param spec The specification list passed by the user.
 * @param private Private flag to limit certain slots to use in NumPy.
 * @return -1 on error 0 on success
 */
static int
fill_arraymethod_from_slots(
        PyBoundArrayMethodObject *res, PyArrayMethod_Spec *spec,
        int private)
{
    // 获取方法对象
    PyArrayMethodObject *meth = res->method;

    /* Set the defaults */
    // 设置默认的 strided loop 获取函数为 npy_default_get_strided_loop
    meth->get_strided_loop = &npy_default_get_strided_loop;
    // 设置默认的解析描述符函数为 default_resolve_descriptors
    meth->resolve_descriptors = &default_resolve_descriptors;
    // 默认没有初始值或者标识
    meth->get_reduction_initial = NULL;  /* no initial/identity by default */

    /* Fill in the slots passed by the user */
    /*
     * TODO: This is reasonable for now, but it would be nice to find a
     *       shorter solution, and add some additional error checking (e.g.
     *       the same slot used twice). Python uses an array of slot offsets.
     */
    # 遍历给定的PyType_Spec结构体的slots数组,每个元素是一个PyType_Slot结构体
    for (PyType_Slot *slot = &spec->slots[0]; slot->slot != 0; slot++) {
        # 根据slot的值进行不同的操作
        switch (slot->slot) {
            case _NPY_METH_resolve_descriptors_with_scalars:
                # 如果不是私有方法,设置运行时错误并返回-1
                if (!private) {
                    PyErr_SetString(PyExc_RuntimeError,
                            "the _NPY_METH_resolve_descriptors_with_scalars "
                            "slot private due to uncertainty about the best "
                            "signature (see gh-24915)");
                    return -1;
                }
                # 设置resolve_descriptors_with_scalars方法为slot中的pfunc函数指针
                meth->resolve_descriptors_with_scalars = slot->pfunc;
                continue;
            case NPY_METH_resolve_descriptors:
                # 设置resolve_descriptors方法为slot中的pfunc函数指针
                meth->resolve_descriptors = slot->pfunc;
                continue;
            case NPY_METH_get_loop:
                """
                 * 注意:公共API中的get_loop被认为是“不稳定的”,
                 *       我不喜欢它的签名,并且不应使用move_references参数。
                 *       (也就是说:我们不应该担心立即更改它,当然这并不会立即中断它。)
                 """
                # 设置get_strided_loop方法为slot中的pfunc函数指针
                meth->get_strided_loop = slot->pfunc;
                continue;
            # "典型"循环,由默认的`get_loop`支持
            case NPY_METH_strided_loop:
                # 设置strided_loop方法为slot中的pfunc函数指针
                meth->strided_loop = slot->pfunc;
                continue;
            case NPY_METH_contiguous_loop:
                # 设置contiguous_loop方法为slot中的pfunc函数指针
                meth->contiguous_loop = slot->pfunc;
                continue;
            case NPY_METH_unaligned_strided_loop:
                # 设置unaligned_strided_loop方法为slot中的pfunc函数指针
                meth->unaligned_strided_loop = slot->pfunc;
                continue;
            case NPY_METH_unaligned_contiguous_loop:
                # 设置unaligned_contiguous_loop方法为slot中的pfunc函数指针
                meth->unaligned_contiguous_loop = slot->pfunc;
                continue;
            case NPY_METH_get_reduction_initial:
                # 设置get_reduction_initial方法为slot中的pfunc函数指针
                meth->get_reduction_initial = slot->pfunc;
                continue;
            case NPY_METH_contiguous_indexed_loop:
                # 设置contiguous_indexed_loop方法为slot中的pfunc函数指针
                meth->contiguous_indexed_loop = slot->pfunc;
                continue;
            case _NPY_METH_static_data:
                # 设置static_data方法为slot中的pfunc函数指针
                meth->static_data = slot->pfunc;
                continue;
            default:
                break;
        }
        # 如果遇到未知的slot编号,抛出运行时错误并返回-1
        PyErr_Format(PyExc_RuntimeError,
                "invalid slot number %d to ArrayMethod: %s",
                slot->slot, spec->name);
        return -1;
    }

    # 检查slots是否有效
    // 检查是否使用默认的解析描述符函数
    if (meth->resolve_descriptors == &default_resolve_descriptors) {
        // 如果设置了casting为-1但未提供默认的`resolve_descriptors`函数,则抛出TypeError异常
        if (spec->casting == -1) {
            PyErr_Format(PyExc_TypeError,
                    "Cannot set casting to -1 (invalid) when not providing "
                    "the default `resolve_descriptors` function. "
                    "(method: %s)", spec->name);
            return -1;
        }
        // 遍历输入和输出的数据类型列表
        for (int i = 0; i < meth->nin + meth->nout; i++) {
            // 如果某个数据类型为NULL
            if (res->dtypes[i] == NULL) {
                // 如果是输入数据类型并且使用了默认的`resolve_descriptors`函数,则抛出TypeError异常
                if (i < meth->nin) {
                    PyErr_Format(PyExc_TypeError,
                            "All input DTypes must be specified when using "
                            "the default `resolve_descriptors` function. "
                            "(method: %s)", spec->name);
                    return -1;
                }
                // 如果没有输入数据并且未指定输出数据类型或使用自定义的`resolve_descriptors`函数,则抛出TypeError异常
                else if (meth->nin == 0) {
                    PyErr_Format(PyExc_TypeError,
                            "Must specify output DTypes or use custom "
                            "`resolve_descriptors` when there are no inputs. "
                            "(method: %s)", spec->name);
                    return -1;
                }
            }
            // 如果是输出数据类型且为参数化数据类型,则必须提供`resolve_descriptors`函数,否则抛出TypeError异常
            if (i >= meth->nin && NPY_DT_is_parametric(res->dtypes[i])) {
                PyErr_Format(PyExc_TypeError,
                        "must provide a `resolve_descriptors` function if any "
                        "output DType is parametric. (method: %s)",
                        spec->name);
                return -1;
            }
        }
    }
    // 如果不是使用默认的获取步进循环函数,则返回0
    if (meth->get_strided_loop != &npy_default_get_strided_loop) {
        /* Do not check the actual loop fields. */
        return 0;
    }

    /* Check whether the provided loops make sense. */

    // 如果方法标志包含NPY_METH_SUPPORTS_UNALIGNED
    if (meth->flags & NPY_METH_SUPPORTS_UNALIGNED) {
        // 如果未提供非对齐步进内部循环,则抛出TypeError异常
        if (meth->unaligned_strided_loop == NULL) {
            PyErr_Format(PyExc_TypeError,
                    "Must provide unaligned strided inner loop when using "
                    "NPY_METH_SUPPORTS_UNALIGNED flag (in method: %s)",
                    spec->name);
            return -1;
        }
    }
    // 如果方法标志不包含NPY_METH_SUPPORTS_UNALIGNED
    else {
        // 如果提供了非对齐步进内部循环,则抛出TypeError异常
        if (meth->unaligned_strided_loop != NULL) {
            PyErr_Format(PyExc_TypeError,
                    "Must not provide unaligned strided inner loop when not "
                    "using NPY_METH_SUPPORTS_UNALIGNED flag (in method: %s)",
                    spec->name);
            return -1;
        }
    }
    /* Fill in the blanks: */
    
    // 如果未提供非对齐连续循环,则使用非对齐步进内部循环
    if (meth->unaligned_contiguous_loop == NULL) {
        meth->unaligned_contiguous_loop = meth->unaligned_strided_loop;
    }
    // 如果未提供步进循环,则使用非对齐步进内部循环
    if (meth->strided_loop == NULL) {
        meth->strided_loop = meth->unaligned_strided_loop;
    }
    // 如果未提供连续循环,则使用步进循环
    if (meth->contiguous_loop == NULL) {
        meth->contiguous_loop = meth->strided_loop;
    }
    # 检查指针变量 `meth` 的 `strided_loop` 成员是否为 NULL
    if (meth->strided_loop == NULL) {
        # 如果为 NULL,则抛出带有特定格式化错误消息的异常
        PyErr_Format(PyExc_TypeError,
                "Must provide a strided inner loop function. (method: %s)",
                spec->name);
        # 返回 -1 表示函数执行失败
        return -1;
    }

    # 如果 `strided_loop` 不为 NULL,表示条件通过,返回 0 表示函数执行成功
    return 0;
/*
 * Public version of `PyArrayMethod_FromSpec_int` (see below).
 *
 * TODO: Error paths will probably need to be improved before a release into
 *       the non-experimental public API.
 */
NPY_NO_EXPORT PyObject *
PyArrayMethod_FromSpec(PyArrayMethod_Spec *spec)
{
    // 遍历输入输出类型列表,确保每个类型都是 PyArrayDTypeMeta_Type 的实例
    for (int i = 0; i < spec->nin + spec->nout; i++) {
        if (!PyObject_TypeCheck(spec->dtypes[i], &PyArrayDTypeMeta_Type)) {
            PyErr_SetString(PyExc_RuntimeError,
                    "ArrayMethod spec contained a non DType.");
            return NULL;
        }
    }
    // 调用内部函数 PyArrayMethod_FromSpec_int 处理具体逻辑,返回处理结果
    return (PyObject *)PyArrayMethod_FromSpec_int(spec, 0);
}


/**
 * Create a new ArrayMethod (internal version).
 *
 * @param spec A filled context object to pass generic information about
 *        the method (such as usually needing the API, and the DTypes).
 *        Unused fields must be NULL.
 * @param private Some slots are currently considered private, if not true,
 *        these will be rejected.
 *
 * @returns A new (bound) ArrayMethod object.
 */
NPY_NO_EXPORT PyBoundArrayMethodObject *
PyArrayMethod_FromSpec_int(PyArrayMethod_Spec *spec, int private)
{
    int nargs = spec->nin + spec->nout;

    // 如果方法名为 NULL,则设置为 "<unknown>"
    if (spec->name == NULL) {
        spec->name = "<unknown>";
    }

    // 验证 spec 结构是否有效,若无效则返回 NULL
    if (validate_spec(spec) < 0) {
        return NULL;
    }

    PyBoundArrayMethodObject *res;
    // 为返回的 PyBoundArrayMethodObject 分配内存空间
    res = PyObject_New(PyBoundArrayMethodObject, &PyBoundArrayMethod_Type);
    if (res == NULL) {
        return NULL;
    }
    res->method = NULL;

    // 为 dtypes 数组分配内存空间,并复制 spec 中的 dtypes
    res->dtypes = PyMem_Malloc(sizeof(PyArray_DTypeMeta *) * nargs);
    if (res->dtypes == NULL) {
        Py_DECREF(res);
        PyErr_NoMemory();
        return NULL;
    }
    for (int i = 0; i < nargs ; i++) {
        Py_XINCREF(spec->dtypes[i]);
        res->dtypes[i] = spec->dtypes[i];
    }

    // 为 res->method 分配内存空间,并初始化其字段
    res->method = PyObject_New(PyArrayMethodObject, &PyArrayMethod_Type);
    if (res->method == NULL) {
        Py_DECREF(res);
        PyErr_NoMemory();
        return NULL;
    }
    memset((char *)(res->method) + sizeof(PyObject), 0,
           sizeof(PyArrayMethodObject) - sizeof(PyObject));

    // 将 spec 中的信息填充到 res->method 中
    res->method->nin = spec->nin;
    res->method->nout = spec->nout;
    res->method->flags = spec->flags;
    res->method->casting = spec->casting;

    // 使用填充函数填充 res 中的 slots 信息
    if (fill_arraymethod_from_slots(res, spec, private) < 0) {
        Py_DECREF(res);
        return NULL;
    }

    // 为方法名字符串分配内存空间,并复制 spec->name
    Py_ssize_t length = strlen(spec->name);
    res->method->name = PyMem_Malloc(length + 1);
    if (res->method->name == NULL) {
        Py_DECREF(res);
        PyErr_NoMemory();
        return NULL;
    }
    strcpy(res->method->name, spec->name);

    return res;
}


static void
arraymethod_dealloc(PyObject *self)
{
    // 释放 PyArrayMethodObject 对象中的 name 字段内存
    PyArrayMethodObject *meth;
    meth = ((PyArrayMethodObject *)self);
    PyMem_Free(meth->name);
}
    # 检查包装方法是否存在,如果存在则进行清理操作(该方法在 umath 中定义)
    if (meth->wrapped_meth != NULL) {
        # 减少包装方法的引用计数,准备释放其内存
        Py_DECREF(meth->wrapped_meth);
        # 遍历包装的数据类型数组,释放每个元素的内存
        for (int i = 0; i < meth->nin + meth->nout; i++) {
            Py_XDECREF(meth->wrapped_dtypes[i]);
        }
        # 释放包装的数据类型数组的内存
        PyMem_Free(meth->wrapped_dtypes);
    }

    # 释放 self 对象所属类型的内存
    Py_TYPE(self)->tp_free(self);
/*
 * 定义了一个名为 PyArrayMethod_Type 的 PyTypeObject 结构体,表示了一个 NumPy 中的数组方法类型。
 * 这个类型结构体包含了类型的基本信息和方法,如名称、基本大小、析构函数、标志等。
 */
NPY_NO_EXPORT PyTypeObject PyArrayMethod_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "numpy._ArrayMethod",  // 类型名称为 "numpy._ArrayMethod"
    .tp_basicsize = sizeof(PyArrayMethodObject),  // 基本大小为 PyArrayMethodObject 结构体的大小
    .tp_dealloc = arraymethod_dealloc,  // 析构函数为 arraymethod_dealloc 函数
    .tp_flags = Py_TPFLAGS_DEFAULT,  // 默认的类型标志
};


/*
 * 定义了一个名为 boundarraymethod_repr 的静态函数,用于返回绑定数组方法对象的字符串表示形式。
 * 函数根据绑定方法对象的 dtypes 属性构造一个表示形式,并返回一个新的 Python 字符串对象。
 */
static PyObject *
boundarraymethod_repr(PyBoundArrayMethodObject *self)
{
    int nargs = self->method->nin + self->method->nout;
    PyObject *dtypes = PyArray_TupleFromItems(
            nargs, (PyObject **)self->dtypes, 0);
    if (dtypes == NULL) {
        return NULL;  // 如果创建元组失败,返回 NULL
    }
    PyObject *repr = PyUnicode_FromFormat(
                        "<np._BoundArrayMethod `%s` for dtypes %S>",
                        self->method->name, dtypes);
    Py_DECREF(dtypes);  // 减少 dtypes 对象的引用计数
    return repr;  // 返回表示形式的字符串对象
}


/*
 * 定义了一个名为 boundarraymethod_dealloc 的静态函数,用于释放绑定数组方法对象所占用的内存。
 * 函数释放 dtypes 数组和 method 对象,并最终释放对象自身所占用的内存。
 */
static void
boundarraymethod_dealloc(PyObject *self)
{
    PyBoundArrayMethodObject *meth;
    meth = ((PyBoundArrayMethodObject *)self);
    int nargs = meth->method->nin + meth->method->nout;

    for (int i = 0; i < nargs; i++) {
        Py_XDECREF(meth->dtypes[i]);  // 释放每个 dtypes 元素对象的引用
    }
    PyMem_Free(meth->dtypes);  // 释放 dtypes 数组的内存

    Py_XDECREF(meth->method);  // 释放 method 对象的引用

    Py_TYPE(self)->tp_free(self);  // 释放对象自身所占用的内存
}


/*
 * 定义了一个名为 boundarraymethod__resolve_descriptors 的静态函数,用于解析描述符并返回相关信息。
 * 函数验证传入的描述符元组是否符合预期格式,并根据验证结果返回相应的结果或错误。
 */
static PyObject *
boundarraymethod__resolve_descripors(
        PyBoundArrayMethodObject *self, PyObject *descr_tuple)
{
    int nin = self->method->nin;
    int nout = self->method->nout;

    PyArray_Descr *given_descrs[NPY_MAXARGS];
    PyArray_Descr *loop_descrs[NPY_MAXARGS];

    if (!PyTuple_CheckExact(descr_tuple) ||
            PyTuple_Size(descr_tuple) != nin + nout) {
        PyErr_Format(PyExc_TypeError,
                "_resolve_descriptors() takes exactly one tuple with as many "
                "elements as the method takes arguments (%d+%d).", nin, nout);
        return NULL;  // 如果描述符元组格式不符合预期,返回错误并 NULL
    }
    for (int i = 0; i < nin + nout; i++) {
        // 获取描述符元组中第 i 个元素
        PyObject *tmp = PyTuple_GetItem(descr_tuple, i);
        // 如果获取失败,返回空指针异常
        if (tmp == NULL) {
            return NULL;
        }
        // 如果获取的元素是 Py_None
        else if (tmp == Py_None) {
            // 如果 i 小于输入个数 nin,设置类型错误并返回空指针异常
            if (i < nin) {
                PyErr_SetString(PyExc_TypeError,
                        "only output dtypes may be omitted (set to None).");
                return NULL;
            }
            // 将给定描述符数组中第 i 个位置设置为 NULL
            given_descrs[i] = NULL;
        }
        // 如果获取的元素是 PyArray_Descr 类型
        else if (PyArray_DescrCheck(tmp)) {
            // 如果获取的类型不是与 self->dtypes[i] 完全相同的类型,设置类型错误并返回空指针异常
            if (Py_TYPE(tmp) != (PyTypeObject *)self->dtypes[i]) {
                PyErr_Format(PyExc_TypeError,
                        "input dtype %S was not an exact instance of the bound "
                        "DType class %S.", tmp, self->dtypes[i]);
                return NULL;
            }
            // 将给定描述符数组中第 i 个位置设置为 tmp 的 PyArray_Descr 类型
            given_descrs[i] = (PyArray_Descr *)tmp;
        }
        // 如果获取的元素类型不是预期的 PyArray_Descr 类型或 Py_None,设置类型错误并返回空指针异常
        else {
            PyErr_SetString(PyExc_TypeError,
                    "dtype tuple can only contain dtype instances or None.");
            return NULL;
        }
    }

    // 初始化视图偏移量为 NPY_MIN_INTP
    npy_intp view_offset = NPY_MIN_INTP;
    // 解析描述符并获取转换规则
    NPY_CASTING casting = self->method->resolve_descriptors(
            self->method, self->dtypes, given_descrs, loop_descrs, &view_offset);

    // 如果转换规则小于 0 并且发生异常,返回空指针异常
    if (casting < 0 && PyErr_Occurred()) {
        return NULL;
    }
    // 如果转换规则小于 0,返回具有整数、Py_None、Py_None 值的元组
    else if (casting < 0) {
        return Py_BuildValue("iOO", casting, Py_None, Py_None);
    }

    // 创建长度为 nin + nout 的元组对象 result_tuple
    PyObject *result_tuple = PyTuple_New(nin + nout);
    // 如果创建元组对象失败,返回空指针异常
    if (result_tuple == NULL) {
        return NULL;
    }
    // 将 loop_descrs 中的对象转移所有权给元组
    for (int i = 0; i < nin + nout; i++) {
        /* transfer ownership to the tuple. */
        PyTuple_SET_ITEM(result_tuple, i, (PyObject *)loop_descrs[i]);
    }

    // 初始化视图偏移量对象为 Py_None
    PyObject *view_offset_obj;
    if (view_offset == NPY_MIN_INTP) {
        Py_INCREF(Py_None);
        view_offset_obj = Py_None;
    }
    // 否则,创建一个 PyLong 对象表示视图偏移量
    else {
        view_offset_obj = PyLong_FromSsize_t(view_offset);
        // 如果创建失败,释放 result_tuple 并返回空指针异常
        if (view_offset_obj == NULL) {
            Py_DECREF(result_tuple);
            return NULL;
        }
    }

    /*
     * The casting flags should be the most generic casting level.
     * If no input is parametric, it must match exactly.
     *
     * (Note that these checks are only debugging checks.)
     */
    // 初始化 parametric 变量为 0,用于检查是否存在参数化输入
    int parametric = 0;
    // 检查 self->dtypes 中是否存在参数化输入,设置 parametric 为 1 并退出循环
    for (int i = 0; i < nin + nout; i++) {
        if (NPY_DT_is_parametric(self->dtypes[i])) {
            parametric = 1;
            break;
        }
    }
    // 如果当前对象的方法中的转换值不为 -1,则执行以下代码块
    if (self->method->casting != -1) {
        // 将当前函数中的 casting 值存入本地变量 cast
        NPY_CASTING cast = casting;
        // 如果当前对象的方法中的转换值与通过 PyArray_MinCastSafety 函数计算的值不匹配
        if (self->method->casting !=
                PyArray_MinCastSafety(cast, self->method->casting)) {
            // 抛出运行时错误,说明描述符解析中的转换级别与存储的不匹配
            PyErr_Format(PyExc_RuntimeError,
                    "resolve_descriptors cast level did not match stored one. "
                    "(set level is %d, got %d for method %s)",
                    self->method->casting, cast, self->method->name);
            // 释放引用并返回 NULL
            Py_DECREF(result_tuple);
            Py_DECREF(view_offset_obj);
            return NULL;
        }
        // 如果不是参数化情况下
        if (!parametric) {
            /*
             * 非参数化情况只有在从等效转换到无转换时才可能不匹配
             * (例如由于字节顺序的变化)。
             */
            // 如果转换级别与当前对象的方法中的转换级别不同,并且当前方法的转换级别不是 NPY_EQUIV_CASTING
            if (cast != self->method->casting &&
                    self->method->casting != NPY_EQUIV_CASTING) {
                // 抛出运行时错误,说明转换级别发生变化,虽然转换是非参数化的,唯一可能的变化只能是从等效到无转换
                PyErr_Format(PyExc_RuntimeError,
                        "resolve_descriptors cast level changed even though "
                        "the cast is non-parametric where the only possible "
                        "change should be from equivalent to no casting. "
                        "(set level is %d, got %d for method %s)",
                        self->method->casting, cast, self->method->name);
                // 释放引用并返回 NULL
                Py_DECREF(result_tuple);
                Py_DECREF(view_offset_obj);
                return NULL;
            }
        }
    }

    // 返回一个 Python 对象,格式化为 "iNN",包括 casting、result_tuple 和 view_offset_obj
    return Py_BuildValue("iNN", casting, result_tuple, view_offset_obj);
/*
 * TODO: This function is not public API, and certain code paths will need
 *       changes and especially testing if they were to be made public.
 */
static PyObject *
boundarraymethod__simple_strided_call(
        PyBoundArrayMethodObject *self, PyObject *arr_tuple)
{
    // 定义存储输入和输出数组、描述符、输出描述符、步长、参数长度等变量
    PyArrayObject *arrays[NPY_MAXARGS];
    PyArray_Descr *descrs[NPY_MAXARGS];
    PyArray_Descr *out_descrs[NPY_MAXARGS];
    char *args[NPY_MAXARGS];
    npy_intp strides[NPY_MAXARGS];
    Py_ssize_t length = -1;
    int aligned = 1;
    int nin = self->method->nin; // 获取输入参数数量
    int nout = self->method->nout; // 获取输出参数数量

    // 检查输入的参数是否为元组,并且元组长度是否等于输入参数数量加上输出参数数量
    if (!PyTuple_CheckExact(arr_tuple) ||
            PyTuple_Size(arr_tuple) != nin + nout) {
        PyErr_Format(PyExc_TypeError,
                "_simple_strided_call() takes exactly one tuple with as many "
                "arrays as the method takes arguments (%d+%d).", nin, nout);
        return NULL;
    }

    // 遍历输入和输出数组
    for (int i = 0; i < nin + nout; i++) {
        // 从元组中获取第i个元素作为输入或输出数组
        PyObject *tmp = PyTuple_GetItem(arr_tuple, i);
        if (tmp == NULL) {
            return NULL; // 获取元素失败,返回错误
        }
        else if (!PyArray_CheckExact(tmp)) {
            PyErr_SetString(PyExc_TypeError,
                    "All inputs must be NumPy arrays.");
            return NULL; // 非NumPy数组,返回错误
        }
        // 将获取的数组强制转换为PyArrayObject类型
        arrays[i] = (PyArrayObject *)tmp;
        // 获取数组的描述符
        descrs[i] = PyArray_DESCR(arrays[i]);

        // 检查输入的描述符是否与绑定的DType类的类型匹配
        if (Py_TYPE(descrs[i]) != (PyTypeObject *)self->dtypes[i]) {
            PyErr_Format(PyExc_TypeError,
                    "input dtype %S was not an exact instance of the bound "
                    "DType class %S.", descrs[i], self->dtypes[i]);
            return NULL; // 描述符类型不匹配,返回错误
        }
        // 检查数组是否为一维数组
        if (PyArray_NDIM(arrays[i]) != 1) {
            PyErr_SetString(PyExc_ValueError,
                    "All arrays must be one dimensional.");
            return NULL; // 非一维数组,返回错误
        }
        // 获取第一个数组的长度作为参考长度
        if (i == 0) {
            length = PyArray_SIZE(arrays[i]);
        }
        else if (PyArray_SIZE(arrays[i]) != length) {
            PyErr_SetString(PyExc_ValueError,
                    "All arrays must have the same length.");
            return NULL; // 数组长度不一致,返回错误
        }
        // 如果是输出数组,检查是否可写
        if (i >= nin) {
            if (PyArray_FailUnlessWriteable(
                    arrays[i], "_simple_strided_call() output") < 0) {
                return NULL; // 输出数组不可写,返回错误
            }
        }

        // 获取数组的数据指针和步长
        args[i] = PyArray_BYTES(arrays[i]);
        strides[i] = PyArray_STRIDES(arrays[i])[0];
        // 检查数组是否是对齐的
        aligned &= PyArray_ISALIGNED(arrays[i]);
    }

    // 如果存在未对齐的数组,并且方法不支持非对齐输入,返回错误
    if (!aligned && !(self->method->flags & NPY_METH_SUPPORTS_UNALIGNED)) {
        PyErr_SetString(PyExc_ValueError,
                "method does not support unaligned input.");
        return NULL;
    }

    // 定义视图偏移和转换模式,解析描述符
    npy_intp view_offset = NPY_MIN_INTP;
    NPY_CASTING casting = self->method->resolve_descriptors(
            self->method, self->dtypes, descrs, out_descrs, &view_offset);
    // 如果 casting 小于 0,表示类型转换失败
    if (casting < 0) {
        // 保存当前的错误类型、值和回溯信息
        PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL;
        PyErr_Fetch(&err_type, &err_value, &err_traceback);
        // 设置类型错误异常并链式传播之前保存的异常信息
        PyErr_SetString(PyExc_TypeError,
                "cannot perform method call with the given dtypes.");
        npy_PyErr_ChainExceptions(err_type, err_value, err_traceback);
        // 返回 NULL 表示异常
        return NULL;
    }

    // 标记是否有类型需要调整
    int dtypes_were_adapted = 0;
    // 遍历输入输出描述符列表,检查是否有需要调整的描述符
    for (int i = 0; i < nin + nout; i++) {
        /* NOTE: This check is probably much stricter than necessary... */
        // 使用按位或运算符检查当前索引的描述符是否需要调整
        dtypes_were_adapted |= descrs[i] != out_descrs[i];
        // 减少输出描述符的引用计数,以便内存管理
        Py_DECREF(out_descrs[i]);
    }
    // 如果有类型需要调整,则设置类型错误异常
    if (dtypes_were_adapted) {
        PyErr_SetString(PyExc_TypeError,
                "_simple_strided_call(): requires dtypes to not require a cast "
                "(must match exactly with `_resolve_descriptors()`).");
        // 返回 NULL 表示异常
        return NULL;
    }

    // 准备数组方法调用的上下文信息
    PyArrayMethod_Context context = {
            .caller = NULL,
            .method = self->method,
            .descriptors = descrs,
    };
    PyArrayMethod_StridedLoop *strided_loop = NULL;
    NpyAuxData *loop_data = NULL;
    NPY_ARRAYMETHOD_FLAGS flags = 0;

    // 获取数组方法的跨步循环函数和相关数据
    if (self->method->get_strided_loop(
            &context, aligned, 0, strides,
            &strided_loop, &loop_data, &flags) < 0) {
        // 如果获取失败,返回 NULL 表示异常
        return NULL;
    }

    /*
     * TODO: 如果有请求,添加浮点数错误检查,并在标志允许的情况下释放 GIL。
     */
    
    // 执行跨步循环函数,并获取返回值
    int res = strided_loop(&context, args, &length, strides, loop_data);
    // 如果存在循环数据,释放其占用的资源
    if (loop_data != NULL) {
        loop_data->free(loop_data);
    }
    // 如果执行结果小于 0,返回 NULL 表示异常
    if (res < 0) {
        return NULL;
    }
    // 返回 Py_None 表示成功执行,没有返回值需要传递
    Py_RETURN_NONE;
/*
 * Support for masked inner-strided loops.  Masked inner-strided loops are
 * only used in the ufunc machinery.  So this special cases them.
 * In the future it probably makes sense to create an::
 *
 *     Arraymethod->get_masked_strided_loop()
 *
 * Function which this can wrap instead.
 */

/*
 * 定义了一个结构体 _masked_stridedloop_data,用于支持带掩码的内部步进循环。
 * 带掩码的内部步进循环仅在ufunc机制中使用。因此这里对它们进行特殊处理。
 * 未来可能会创建一个函数 Arraymethod->get_masked_strided_loop() 来替代它。
 */
typedef struct {
    NpyAuxData base;
    PyArrayMethod_StridedLoop *unmasked_stridedloop;
    NpyAuxData *unmasked_auxdata;
    int nargs;
    char *dataptrs[];
} _masked_stridedloop_data;

/*
 * 释放 _masked_stridedloop_data 类型的辅助数据的函数
 */
static void
_masked_stridedloop_data_free(NpyAuxData *auxdata)
{
    _masked_stridedloop_data *data = (_masked_stridedloop_data *)auxdata;
    NPY_AUXDATA_FREE(data->unmasked_auxdata);
    PyMem_Free(data);
}

/*
 * 这个函数将一个常规的未掩码步进循环封装成带掩码的步进循环,仅对掩码为True的元素调用函数。
 *
 * TODO: Reducers(减少器)也使用此代码来实现带掩码的减少操作。
 *       在合并它们之前,reducers 对广播有一个特例:当掩码步幅为0时,代码不会像 npy_memchr 当前那样检查所有元素。
 *       如果广播掩码足够普遍,重新添加这样的优化可能是值得的。
 */
static int
generic_masked_strided_loop(PyArrayMethod_Context *context,
        char *const *data, const npy_intp *dimensions,
        const npy_intp *strides, NpyAuxData *_auxdata)
{
    _masked_stridedloop_data *auxdata = (_masked_stridedloop_data *)_auxdata;
    int nargs = auxdata->nargs;
    PyArrayMethod_StridedLoop *strided_loop = auxdata->unmasked_stridedloop;
    NpyAuxData *strided_loop_auxdata = auxdata->unmasked_auxdata;

    char **dataptrs = auxdata->dataptrs;
    memcpy(dataptrs, data, nargs * sizeof(char *));
    char *mask = data[nargs];
    npy_intp mask_stride = strides[nargs];

    npy_intp N = dimensions[0];
    /* Process the data as runs of unmasked values */
    do {
        Py_ssize_t subloopsize;

        /* Skip masked values */
        mask = npy_memchr(mask, 0, mask_stride, N, &subloopsize, 1);
        for (int i = 0; i < nargs; i++) {
            dataptrs[i] += subloopsize * strides[i];
        }
        N -= subloopsize;

        /* Process unmasked values */
        mask = npy_memchr(mask, 0, mask_stride, N, &subloopsize, 0);
        if (subloopsize > 0) {
            int res = strided_loop(context,
                    dataptrs, &subloopsize, strides, strided_loop_auxdata);
            if (res != 0) {
                return res;
            }
            for (int i = 0; i < nargs; i++) {
                dataptrs[i] += subloopsize * strides[i];
            }
            N -= subloopsize;
        }
    } while (N > 0);

    return 0;
}
/*
 * Fetches a strided-loop function that supports a boolean mask as additional
 * (last) operand to the strided-loop.  It is otherwise largely identical to
 * the `get_strided_loop` method which it wraps.
 * This is the core implementation for the ufunc `where=...` keyword argument.
 *
 * NOTE: This function does not support `move_references` or inner dimensions.
 */
NPY_NO_EXPORT int
PyArrayMethod_GetMaskedStridedLoop(
        PyArrayMethod_Context *context,
        int aligned, npy_intp *fixed_strides,
        PyArrayMethod_StridedLoop **out_loop,
        NpyAuxData **out_transferdata,
        NPY_ARRAYMETHOD_FLAGS *flags)
{
    _masked_stridedloop_data *data;
    int nargs = context->method->nin + context->method->nout;

    /* Allocate memory for _masked_stridedloop_data struct */
    data = PyMem_Malloc(sizeof(_masked_stridedloop_data) +
                        sizeof(char *) * nargs);
    if (data == NULL) {
        PyErr_NoMemory();  /* Raise memory error if allocation fails */
        return -1;  /* Return -1 to indicate failure */
    }
    data->base.free = _masked_stridedloop_data_free;  /* Set free function */
    data->base.clone = NULL;  /* Set clone function as not used */
    data->unmasked_stridedloop = NULL;  /* Initialize unmasked strided loop */

    /* Retrieve unmasked strided loop using context method */
    if (context->method->get_strided_loop(context,
            aligned, 0, fixed_strides,
            &data->unmasked_stridedloop, &data->unmasked_auxdata, flags) < 0) {
        PyMem_Free(data);  /* Free allocated memory on failure */
        return -1;  /* Return -1 to indicate failure */
    }
    *out_transferdata = (NpyAuxData *)data;  /* Set output transfer data */
    *out_loop = generic_masked_strided_loop;  /* Set output loop function */
    return 0;  /* Return 0 to indicate success */
}


/* Definition of methods associated with PyBoundArrayMethodObject */
PyMethodDef boundarraymethod_methods[] = {
    {"_resolve_descriptors", (PyCFunction)boundarraymethod__resolve_descripors,
     METH_O, "Resolve the given dtypes."},
    {"_simple_strided_call", (PyCFunction)boundarraymethod__simple_strided_call,
     METH_O, "call on 1-d inputs and pre-allocated outputs (single call)."},
    {NULL, 0, 0, NULL},  /* Sentinel indicating end of method definitions */
};


/* Function to retrieve whether the method supports unaligned inputs/outputs */
static PyObject *
boundarraymethod__supports_unaligned(PyBoundArrayMethodObject *self)
{
    return PyBool_FromLong(self->method->flags & NPY_METH_SUPPORTS_UNALIGNED);
}


/* Getter definitions for PyBoundArrayMethodObject attributes */
PyGetSetDef boundarraymethods_getters[] = {
    {"_supports_unaligned",
     (getter)boundarraymethod__supports_unaligned, NULL,
     "whether the method supports unaligned inputs/outputs.", NULL},
    {NULL, NULL, NULL, NULL, NULL},  /* Sentinel indicating end of getter definitions */
};


/* Type definition for PyBoundArrayMethod_Type */
NPY_NO_EXPORT PyTypeObject PyBoundArrayMethod_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)  /* Initialize base object */
    .tp_name = "numpy._BoundArrayMethod",  /* Type name */
    .tp_basicsize = sizeof(PyBoundArrayMethodObject),  /* Size of the object */
    .tp_dealloc = boundarraymethod_dealloc,  /* Deallocation function */
    .tp_repr = (reprfunc)boundarraymethod_repr,  /* Representation function */
    .tp_flags = Py_TPFLAGS_DEFAULT,  /* Default flags */
    .tp_methods = boundarraymethod_methods,  /* Methods associated */
    .tp_getset = boundarraymethods_getters,  /* Getters and setters */
};

.\numpy\numpy\_core\src\multiarray\array_method.h

#ifndef NUMPY_CORE_SRC_MULTIARRAY_ARRAY_METHOD_H_
#define NUMPY_CORE_SRC_MULTIARRAY_ARRAY_METHOD_H_

// 定义防止使用过时 API 的宏
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
// 定义 _MULTIARRAYMODULE 宏
#define _MULTIARRAYMODULE

// 包含 Python 标准头文件
#include <Python.h>
// 包含 NumPy 数组类型相关的头文件
#include <numpy/ndarraytypes.h>

#ifdef __cplusplus
extern "C" {
#endif

// 包含 NumPy 的 dtype API 头文件
#include "numpy/dtype_api.h"

/*
 * 以下是 PyArrayMethod_MINIMAL_FLAGS 宏的定义:
 * 默认最小标志位,目前仅指定了 NPY_METH_NO_FLOATINGPOINT_ERRORS
 */
#define PyArrayMethod_MINIMAL_FLAGS NPY_METH_NO_FLOATINGPOINT_ERRORS

/*
 * 定义 PyArrayMethod_COMBINED_FLAGS 宏:
 * 组合两个输入标志位,去除 PyArrayMethod_MINIMAL_FLAGS,然后合并剩余部分
 */
#define PyArrayMethod_COMBINED_FLAGS(flags1, flags2)  \
        ((NPY_ARRAYMETHOD_FLAGS)(  \
            ((flags1 | flags2) & ~PyArrayMethod_MINIMAL_FLAGS)  \
            | (flags1 & flags2)))

/*
 * 定义 PyArrayMethodObject_tag 结构体:
 * 该结构体描述了数组方法的属性和函数指针,不建议公开
 */
typedef struct PyArrayMethodObject_tag {
    PyObject_HEAD
    char *name;  // 方法名称
    int nin, nout;  // 输入和输出参数个数
    NPY_CASTING casting;  // 类型转换方式
    NPY_ARRAYMETHOD_FLAGS flags;  // 方法的标志位
    void *static_data;  // 方法可能需要的静态数据指针
    // 以下是用于解析描述符和获取循环的函数指针
    PyArrayMethod_ResolveDescriptorsWithScalar *resolve_descriptors_with_scalars;
    PyArrayMethod_ResolveDescriptors *resolve_descriptors;
    PyArrayMethod_GetLoop *get_strided_loop;
    PyArrayMethod_GetReductionInitial *get_reduction_initial;
    // 典型的循环函数指针
    PyArrayMethod_StridedLoop *strided_loop;
    PyArrayMethod_StridedLoop *contiguous_loop;
    PyArrayMethod_StridedLoop *unaligned_strided_loop;
    PyArrayMethod_StridedLoop *unaligned_contiguous_loop;
    PyArrayMethod_StridedLoop *contiguous_indexed_loop;
    // 用于包装在 umath 中定义的数组方法的结构体指针
    struct PyArrayMethodObject_tag *wrapped_meth;
    PyArray_DTypeMeta **wrapped_dtypes;  // 包装的数据类型数组
    // 用于翻译给定描述符和循环描述符的函数指针
    PyArrayMethod_TranslateGivenDescriptors *translate_given_descrs;
    PyArrayMethod_TranslateLoopDescriptors *translate_loop_descrs;
    // 保留给遗留回退数组方法使用的区块
    char legacy_initial[sizeof(npy_clongdouble)];  // 初始值存储
} PyArrayMethodObject;

#endif  // NUMPY_CORE_SRC_MULTIARRAY_ARRAY_METHOD_H_
/*
 * We will sometimes have to create a ArrayMethod and allow passing it around,
 * similar to `instance.method` returning a bound method, e.g. a function like
 * `ufunc.resolve()` can return a bound object.
 * The current main purpose of the BoundArrayMethod is that it holds on to the
 * `dtypes` (the classes), so that the `ArrayMethod` (e.g. for casts) will
 * not create references cycles.  In principle, it could hold any information
 * which is also stored on the ufunc (and thus does not need to be repeated
 * on the `ArrayMethod` itself.
 */
typedef struct {
    PyObject_HEAD
    // 指向 PyArray_DTypeMeta 指针的数组,用于存储数据类型信息
    PyArray_DTypeMeta **dtypes;
    // 指向 PyArrayMethodObject 对象的指针,表示绑定的数组方法对象
    PyArrayMethodObject *method;
} PyBoundArrayMethodObject;


// 外部声明 PyArrayMethod_Type 类型
extern NPY_NO_EXPORT PyTypeObject PyArrayMethod_Type;
// 外部声明 PyBoundArrayMethod_Type 类型
extern NPY_NO_EXPORT PyTypeObject PyBoundArrayMethod_Type;


/*
 * Used internally (initially) for real to complex loops only
 */
// NPY_NO_EXPORT 指示该函数在本文件中使用,作用是获取默认的分步循环函数
NPY_NO_EXPORT int
npy_default_get_strided_loop(
        PyArrayMethod_Context *context,
        int aligned, int NPY_UNUSED(move_references), const npy_intp *strides,
        PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata,
        NPY_ARRAYMETHOD_FLAGS *flags);


// NPY_NO_EXPORT 指示该函数在本文件中使用,作用是获取带掩码的分步循环函数
NPY_NO_EXPORT int
PyArrayMethod_GetMaskedStridedLoop(
        PyArrayMethod_Context *context,
        int aligned,
        npy_intp *fixed_strides,
        PyArrayMethod_StridedLoop **out_loop,
        NpyAuxData **out_transferdata,
        NPY_ARRAYMETHOD_FLAGS *flags);


// NPY_NO_EXPORT 指示该函数在本文件中使用,作用是根据规范创建数组方法对象
NPY_NO_EXPORT PyObject *
PyArrayMethod_FromSpec(PyArrayMethod_Spec *spec);


/*
 * TODO: This function is the internal version, and its error paths may
 *       need better tests when a public version is exposed.
 */
// NPY_NO_EXPORT 指示该函数在本文件中使用,作用是根据规范创建内部版本的绑定数组方法对象
NPY_NO_EXPORT PyBoundArrayMethodObject *
PyArrayMethod_FromSpec_int(PyArrayMethod_Spec *spec, int priv);

#ifdef __cplusplus
}
#endif

#endif  /* NUMPY_CORE_SRC_MULTIARRAY_ARRAY_METHOD_H_ */

.\numpy\numpy\_core\src\multiarray\buffer.c

/*
 * 定义 NPY_NO_DEPRECATED_API,避免使用已废弃的 NumPy API 版本
 * 定义 _MULTIARRAYMODULE,暂未说明具体用途
 */
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE

/*
 * 清除 PY_SSIZE_T_CLEAN 宏定义,确保 Py_ssize_t 被正确声明
 */
#define PY_SSIZE_T_CLEAN

/*
 * 包含 Python.h 头文件,提供 Python C API 支持
 * 包含 structmember.h 头文件,用于定义结构体成员
 */
#include <Python.h>
#include <structmember.h>

/*
 * 包含 NumPy 相关头文件
 */
#include "numpy/arrayobject.h"
#include "numpy/arrayscalars.h"

/*
 * 包含 NumPy 的配置文件 npy_config.h
 */
#include "npy_config.h"

/*
 * 包含 NumPy 的 Python 兼容性支持文件 npy_pycompat.h
 */
#include "npy_pycompat.h"

/*
 * 包含 NumPy 的缓冲区协议实现 npy_buffer.h
 * 包含 NumPy 的通用函数和宏定义 common.h
 * 包含 NumPy 的操作系统相关功能 numpyos.h
 * 包含 NumPy 的数组对象定义 arrayobject.h
 * 包含 NumPy 的标量类型定义 scalartypes.h
 * 包含 NumPy 的数据类型元数据定义 dtypemeta.h
 */

/*************************************************************************
 ****************   实现缓冲区协议 ****************************
 *************************************************************************/

/*************************************************************************
 * PEP 3118 缓冲区协议
 *
 * 实现 PEP 3118 有些复杂,因为需要考虑以下要求:
 *
 * - 不向 ndarray 或 descr 结构体添加新成员,以保持二进制兼容性。
 *   (同时,向其中添加项目实际上并不是很有用,因为可变性问题阻碍了数组与缓冲区视图之间的一对一关系。)
 *
 * - 不使用 bf_releasebuffer,因为这会阻止 PyArg_ParseTuple("s#", ... 的工作。
 *   违反这一点会导致在 Python 2.6 上引起多个向后兼容性问题。
 *
 * - 在数组原地重塑或其 dtype 被修改时,要正确处理。
 *
 * 下面采取的解决方案是手动跟踪为 Py_buffers 分配的内存。
 *************************************************************************/

/*
 * 格式字符串转换器
 *
 * 将 PyArray_Descr 转换为 PEP 3118 格式字符串。
 */

/* 快速字符串 'class' */
typedef struct {
    char *s;
    size_t allocated;
    size_t pos;
} _tmp_string_t;

#define INIT_SIZE   16

/*
 * 将字符 c 追加到字符串 s 中
 */
static int
_append_char(_tmp_string_t *s, char c)
{
    if (s->pos >= s->allocated) {
        char *p;
        size_t to_alloc = (s->allocated == 0) ? INIT_SIZE : (2 * s->allocated);

        p = PyObject_Realloc(s->s, to_alloc);
        if (p == NULL) {
            PyErr_SetString(PyExc_MemoryError, "memory allocation failed");
            return -1;
        }
        s->s = p;
        s->allocated = to_alloc;
    }
    s->s[s->pos] = c;
    ++s->pos;
    return 0;
}

/*
 * 将字符串 p 追加到字符串 s 中
 */
static int
_append_str(_tmp_string_t *s, char const *p)
{
    for (; *p != '\0'; p++) {
        if (_append_char(s, *p) < 0) {
            return -1;
        }
    }
    return 0;
}

/*
 * 向字符串 str 追加一个 PEP3118 格式的字段名 ":name:"
 */
static int
_append_field_name(_tmp_string_t *str, PyObject *name)
{
    int ret = -1;
    char *p;
    Py_ssize_t len;
    PyObject *tmp;
    /* FIXME: XXX -- should it use UTF-8 here? */
    tmp = PyUnicode_AsUTF8String(name);
    if (tmp == NULL || PyBytes_AsStringAndSize(tmp, &p, &len) < 0) {
        PyErr_Clear();
        PyErr_SetString(PyExc_ValueError, "invalid field name");
        goto fail;
    }
    if (_append_char(str, ':') < 0) {
        goto fail;
    }
    while (len > 0) {
        // 当还有字符需要处理时进入循环
        if (*p == ':') {
            // 如果当前字符为':',则抛出值错误异常并提示不允许在缓冲字段名中使用':'
            PyErr_SetString(PyExc_ValueError,
                            "':' is not an allowed character in buffer "
                            "field names");
            // 跳转到失败标签,表示处理失败
            goto fail;
        }
        // 将当前字符追加到字符串对象中,如果追加失败则跳转到失败标签
        if (_append_char(str, *p) < 0) {
            goto fail;
        }
        // 移动到下一个字符
        ++p;
        // 减少待处理字符数
        --len;
    }
    // 在字符串对象末尾追加字符':',如果失败则跳转到失败标签
    if (_append_char(str, ':') < 0) {
        goto fail;
    }
    // 设置返回值为0,表示成功处理完所有字符
    ret = 0;
fail:
    // 释放临时对象的 Python 引用计数
    Py_XDECREF(tmp);
    // 返回操作的结果
    return ret;
}

/*
 * 如果一个类型在给定数组的每个项中都是对齐的,
 * 并且描述符元素大小是对齐的倍数,
 * 并且数组数据按照对齐的粒度定位,则返回非零值。
 */
static inline int
_is_natively_aligned_at(PyArray_Descr *descr,
                        PyArrayObject *arr, Py_ssize_t offset)
{
    int k;

    if (NPY_LIKELY(descr == PyArray_DESCR(arr))) {
        /*
         * 如果描述符与数组的描述符相同,可以假设数组的对齐是正确的。
         */
        assert(offset == 0);
        if (PyArray_ISALIGNED(arr)) {
            assert(descr->elsize % descr->alignment == 0);
            return 1;
        }
        return 0;
    }

    if ((Py_ssize_t)(PyArray_DATA(arr)) % descr->alignment != 0) {
        return 0;
    }

    if (offset % descr->alignment != 0) {
        return 0;
    }

    if (descr->elsize % descr->alignment) {
        return 0;
    }

    for (k = 0; k < PyArray_NDIM(arr); ++k) {
        if (PyArray_DIM(arr, k) > 1) {
            if (PyArray_STRIDE(arr, k) % descr->alignment != 0) {
                return 0;
            }
        }
    }

    return 1;
}

/*
 * 根据描述符填充字符串 str,生成适合 PEP 3118 格式的字符串。
 * 对于结构化数据类型,递归调用自身。每次调用在偏移量上扩展 str,
 * 更新偏移量,并使用 descr->byteorder(以及可能的 obj 中的字节顺序)
 * 来确定字节顺序字符。
 *
 * 返回 0 表示成功,-1 表示失败
 */
static int
_buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str,
                      PyObject* obj, Py_ssize_t *offset,
                      char *active_byteorder)
{
    int k;
    char _active_byteorder = '@'; // 默认的字节顺序字符
    Py_ssize_t _offset = 0; // 默认的偏移量

    if (active_byteorder == NULL) {
        active_byteorder = &_active_byteorder;
    }
    if (offset == NULL) {
        offset = &_offset;
    }
    # 检查描述符是否具有子数组
    if (PyDataType_HASSUBARRAY(descr)) {
        # 将描述符转换为旧版数组描述符类型
        _PyArray_LegacyDescr *ldescr = (_PyArray_LegacyDescr *)descr;

        # 定义变量:子项对象、子数组元组、总元素计数、维度大小、旧偏移量、字符缓冲区和返回状态
        PyObject *item, *subarray_tuple;
        Py_ssize_t total_count = 1;
        Py_ssize_t dim_size;
        Py_ssize_t old_offset;
        char buf[128];
        int ret;

        # 检查子数组形状是否为元组,若是则直接使用,否则构建一个包含形状的元组
        if (PyTuple_Check(ldescr->subarray->shape)) {
            subarray_tuple = ldescr->subarray->shape;
            Py_INCREF(subarray_tuple);
        }
        else {
            subarray_tuple = Py_BuildValue("(O)", ldescr->subarray->shape);
        }

        # 在字符串缓冲中追加左括号 '('
        if (_append_char(str, '(') < 0) {
            ret = -1;
            goto subarray_fail;
        }
        # 遍历子数组元组
        for (k = 0; k < PyTuple_GET_SIZE(subarray_tuple); ++k) {
            # 如果不是第一个元素,追加逗号 ','
            if (k > 0) {
                if (_append_char(str, ',') < 0) {
                    ret = -1;
                    goto subarray_fail;
                }
            }
            # 获取子数组元组中的项
            item = PyTuple_GET_ITEM(subarray_tuple, k);
            # 将项转换为 Py_ssize_t 类型的维度大小
            dim_size = PyNumber_AsSsize_t(item, NULL);

            # 将维度大小转换为字符串,存入 buf 中
            PyOS_snprintf(buf, sizeof(buf), "%ld", (long)dim_size);
            # 将 buf 中的内容追加到字符串缓冲 str 中
            if (_append_str(str, buf) < 0) {
                ret = -1;
                goto subarray_fail;
            }
            # 计算总元素个数
            total_count *= dim_size;
        }
        # 在字符串缓冲中追加右括号 ')'
        if (_append_char(str, ')') < 0) {
            ret = -1;
            goto subarray_fail;
        }

        # 保存旧偏移量,调用 _buffer_format_string 处理子数组的基础类型描述符
        old_offset = *offset;
        ret = _buffer_format_string(ldescr->subarray->base, str, obj, offset,
                                    active_byteorder);
        # 更新偏移量,考虑到子数组的总元素个数
        *offset = old_offset + (*offset - old_offset) * total_count;

    subarray_fail:
        # 减少子数组元组的引用计数,并返回操作状态
        Py_DECREF(subarray_tuple);
        return ret;
    }
    else if (PyDataType_HASFIELDS(descr)) {
        // 如果描述符包含字段信息
        _PyArray_LegacyDescr *ldescr = (_PyArray_LegacyDescr *)descr;
        // 将偏移量保存到基础偏移量中
        Py_ssize_t base_offset = *offset;

        // 向字符串中追加 'T{',表示开始一个复合类型描述
        if (_append_str(str, "T{") < 0) return -1;
        // 遍历复合类型中的字段
        for (k = 0; k < PyTuple_GET_SIZE(ldescr->names); ++k) {
            PyObject *name, *item, *offset_obj;
            PyArray_Descr *child;
            Py_ssize_t new_offset;
            int ret;

            // 获取字段名
            name = PyTuple_GET_ITEM(ldescr->names, k);
            // 从字段字典中获取字段信息
            item = PyDict_GetItem(ldescr->fields, name);

            // 获取字段的数据类型描述符和偏移量对象
            child = (PyArray_Descr*)PyTuple_GetItem(item, 0);
            offset_obj = PyTuple_GetItem(item, 1);
            // 将偏移量对象转换为 Py_ssize_t 类型
            new_offset = PyLong_AsLong(offset_obj);
            // 检查是否转换出错
            if (error_converting(new_offset)) {
                return -1;
            }
            // 根据基础偏移量计算新的偏移量
            new_offset += base_offset;

            /* 手动插入填充 */
            // 如果当前偏移量大于新的偏移量,说明需要插入填充字节
            if (*offset > new_offset) {
                PyErr_SetString(
                    PyExc_ValueError,
                    "dtypes with overlapping or out-of-order fields are not "
                    "representable as buffers. Consider reordering the fields."
                );
                return -1;
            }
            // 插入填充字节,直到当前偏移量达到新的偏移量
            while (*offset < new_offset) {
                if (_append_char(str, 'x') < 0) return -1;
                ++*offset;
            }

            /* 插入子项 */
            // 递归处理子项的格式字符串
            ret = _buffer_format_string(child, str, obj, offset,
                                  active_byteorder);
            // 检查处理子项是否出错
            if (ret < 0) {
                return -1;
            }

            /* 插入字段名 */
            // 向字符串中插入字段名
            if (_append_field_name(str, name) < 0) return -1;
        }
        // 向字符串中追加 '}',表示复合类型描述结束
        if (_append_char(str, '}') < 0) return -1;
    }
    // 返回成功标志
    return 0;
/* 结构体定义:用于存储为提供缓冲区接口而需要的每个数组的额外数据 */
typedef struct _buffer_info_t_tag {
    char *format;               // 缓冲区数据的格式
    int ndim;                   // 数组的维度
    Py_ssize_t *strides;        // 数组的步长信息
    Py_ssize_t *shape;          // 数组的形状信息
    struct _buffer_info_t_tag *next;   // 指向下一个缓冲区信息结构体的指针
} _buffer_info_t;

/* 创建并返回一个新的缓冲区信息结构体 */
static _buffer_info_t*
_buffer_info_new(PyObject *obj, int flags)
{
    /* 缓冲区信息被缓存为 PyLongObjects,这使得它们在 valgrind 中看起来像是无法访问的丢失内存 */
    _buffer_info_t *info;       // 缓冲区信息结构体指针
    _tmp_string_t fmt = {NULL, 0, 0};   // 临时字符串结构体
    int k;                      // 循环变量
    PyArray_Descr *descr = NULL;    // 数组描述符指针
    int err = 0;                // 错误码

    /* 如果 obj 是一个标量对象且类型是 Void */
    if (PyArray_IsScalar(obj, Void)) {
        info = PyObject_Malloc(sizeof(_buffer_info_t));   // 分配缓冲区信息结构体的内存空间
        if (info == NULL) {
            PyErr_NoMemory();   // 内存分配失败,抛出内存错误异常
            goto fail;          // 跳转到失败处理标签
        }
        info->ndim = 0;         // 设置数组维度为 0
        info->shape = NULL;     // 形状信息为空
        info->strides = NULL;   // 步长信息为空

        /* 从标量对象创建数组描述符 */
        descr = PyArray_DescrFromScalar(obj);
        if (descr == NULL) {
            goto fail;          // 如果创建描述符失败,跳转到失败处理标签
        }
    }
    else {
        // 断言对象是否为 NumPy 数组
        assert(PyArray_Check(obj));
        // 将对象转换为 PyArrayObject 类型
        PyArrayObject * arr = (PyArrayObject *)obj;
    
        // 分配内存给 info 结构体,包括 shape 和 strides
        info = PyObject_Malloc(sizeof(_buffer_info_t) +
                               sizeof(Py_ssize_t) * PyArray_NDIM(arr) * 2);
        // 如果内存分配失败,设置内存错误并跳转到失败标签
        if (info == NULL) {
            PyErr_NoMemory();
            goto fail;
        }
        /* 填充 shape 和 strides */
        info->ndim = PyArray_NDIM(arr);
    
        // 如果数组维度为 0,shape 和 strides 都为 NULL
        if (info->ndim == 0) {
            info->shape = NULL;
            info->strides = NULL;
        }
        else {
            // 分配内存给 shape 和 strides,确保地址对齐
            info->shape = (npy_intp *)((char *)info + sizeof(_buffer_info_t));
            assert((size_t)info->shape % sizeof(npy_intp) == 0);
            info->strides = info->shape + PyArray_NDIM(arr);
    
            /*
             * 一些缓冲区使用者可能希望连续的缓冲区在维度为 1 时也有格式良好的 strides,
             * 但是我们在内部不保证这一点。因此,对于连续数组,需要重新计算 strides。
             */
            int f_contiguous = (flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS;
            if (PyArray_IS_C_CONTIGUOUS(arr) && !(
                    f_contiguous && PyArray_IS_F_CONTIGUOUS(arr))) {
                Py_ssize_t sd = PyArray_ITEMSIZE(arr);
                for (k = info->ndim-1; k >= 0; --k) {
                    info->shape[k] = PyArray_DIMS(arr)[k];
                    info->strides[k] = sd;
                    sd *= info->shape[k];
                }
            }
            else if (PyArray_IS_F_CONTIGUOUS(arr)) {
                Py_ssize_t sd = PyArray_ITEMSIZE(arr);
                for (k = 0; k < info->ndim; ++k) {
                    info->shape[k] = PyArray_DIMS(arr)[k];
                    info->strides[k] = sd;
                    sd *= info->shape[k];
                }
            }
            else {
                // 非连续数组,直接复制 PyArray 的维度和 strides
                for (k = 0; k < PyArray_NDIM(arr); ++k) {
                    info->shape[k] = PyArray_DIMS(arr)[k];
                    info->strides[k] = PyArray_STRIDES(arr)[k];
                }
            }
        }
        descr = PyArray_DESCR(arr);
        // 增加对象的引用计数
        Py_INCREF(descr);
    }
    
    /* 填充格式 */
    // 如果 flags 包含 PyBUF_FORMAT 标志位
    if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) {
        // 获取数组的格式字符串
        err = _buffer_format_string(descr, &fmt, obj, NULL, NULL);
        // 减少描述符的引用计数
        Py_DECREF(descr);
        // 如果获取格式字符串出错,跳转到失败标签
        if (err != 0) {
            goto fail;
        }
        // 如果追加字符失败,跳转到失败标签
        if (_append_char(&fmt, '\0') < 0) {
            goto fail;
        }
        // 将格式字符串赋值给 info 的 format 字段
        info->format = fmt.s;
    }
    else {
        // 减少描述符的引用计数
        Py_DECREF(descr);
        // 将 format 字段设置为 NULL
        info->format = NULL;
    }
    info->next = NULL;
    // 返回 info 结构体
    return info;
fail:
    # 释放 fmt.s 所占用的内存
    PyObject_Free(fmt.s);
    # 释放 info 所占用的内存
    PyObject_Free(info);
    # 返回 NULL 表示操作失败
    return NULL;
}

/* Compare two info structures */
static Py_ssize_t
_buffer_info_cmp(_buffer_info_t *a, _buffer_info_t *b)
{
    Py_ssize_t c;
    int k;

    // 比较两个 _buffer_info_t 结构体的 format 字段
    if (a->format != NULL && b->format != NULL) {
        c = strcmp(a->format, b->format);
        if (c != 0) return c;
    }
    // 比较两个 _buffer_info_t 结构体的 ndim 字段
    c = a->ndim - b->ndim;
    if (c != 0) return c;

    // 逐个比较两个 _buffer_info_t 结构体的 shape 和 strides 数组
    for (k = 0; k < a->ndim; ++k) {
        c = a->shape[k] - b->shape[k];
        if (c != 0) return c;
        c = a->strides[k] - b->strides[k];
        if (c != 0) return c;
    }

    // 结构体比较完成,返回相等
    return 0;
}


/*
 * Tag the buffer info pointer by adding 2 (unless it is NULL to simplify
 * object initialization).
 * The linked list of buffer-infos was appended to the array struct in
 * NumPy 1.20. Tagging the pointer gives us a chance to raise/print
 * a useful error message instead of crashing hard if a C-subclass uses
 * the same field.
 */
static inline void *
buffer_info_tag(void *buffer_info)
{
    // 如果 buffer_info 为 NULL,则直接返回 NULL
    if (buffer_info == NULL) {
        return buffer_info;
    }
    else {
        // 否则,在 buffer_info 地址上加 3 并返回,用于标记
        return (void *)((uintptr_t)buffer_info + 3);
    }
}


static inline int
_buffer_info_untag(
        void *tagged_buffer_info, _buffer_info_t **buffer_info, PyObject *obj)
{
    // 如果 tagged_buffer_info 为 NULL,则直接返回,无需解标记
    if (tagged_buffer_info == NULL) {
        *buffer_info = NULL;
        return 0;
    }
    // 检查 tagged_buffer_info 是否正确标记
    if (NPY_UNLIKELY(((uintptr_t)tagged_buffer_info & 0x7) != 3)) {
        PyErr_Format(PyExc_RuntimeError,
                "Object of type %S appears to be C subclassed NumPy array, "
                "void scalar, or allocated in a non-standard way."
                "NumPy reserves the right to change the size of these "
                "structures. Projects are required to take this into account "
                "by either recompiling against a specific NumPy version or "
                "padding the struct and enforcing a maximum NumPy version.",
                Py_TYPE(obj));
        return -1;
    }
    // 解标记 tagged_buffer_info 并返回正确的 _buffer_info_t 结构体指针
    *buffer_info = (void *)((uintptr_t)tagged_buffer_info - 3);
    return 0;
}


/*
 * NOTE: for backward compatibility (esp. with PyArg_ParseTuple("s#", ...))
 * we do *not* define bf_releasebuffer at all.
 *
 * Instead, any extra data allocated with the buffer is released only in
 * array_dealloc.
 *
 * Ensuring that the buffer stays in place is taken care by refcounting;
 * ndarrays do not reallocate if there are references to them, and a buffer
 * view holds one reference.
 *
 * This is stored in the array's _buffer_info slot (currently as a void *).
 */
static void
_buffer_info_free_untagged(void *_buffer_info)
{
    _buffer_info_t *next = _buffer_info;
    // 释放整个链表上的 _buffer_info_t 结构体及其包含的内存
    while (next != NULL) {
        _buffer_info_t *curr = next;
        next = curr->next;
        if (curr->format) {
            PyObject_Free(curr->format);
        }
        /* Shape is allocated as part of info */
        PyObject_Free(curr);
    }
}
/*
 * 检查指针是否已标记,并释放缓存列表。
 * (标记检查仅适用于由于结构大小在1.20中更改而进行的过渡)
 */
NPY_NO_EXPORT int
_buffer_info_free(void *buffer_info, PyObject *obj)
{
    _buffer_info_t *untagged_buffer_info;
    // 如果解除标记失败,则返回-1
    if (_buffer_info_untag(buffer_info, &untagged_buffer_info, obj) < 0) {
        return -1;
    }
    // 释放未标记的缓存信息
    _buffer_info_free_untagged(untagged_buffer_info);
    return 0;
}




/*
 * 获取缓冲区信息,返回传入的旧信息或添加保持(因此替换)旧信息的新缓冲区信息。
 */
static _buffer_info_t*
_buffer_get_info(void **buffer_info_cache_ptr, PyObject *obj, int flags)
{
    _buffer_info_t *info = NULL;
    _buffer_info_t *stored_info;  /* 当前存储的第一个缓冲区信息 */

    // 如果解除标记失败,则返回空指针
    if (_buffer_info_untag(*buffer_info_cache_ptr, &stored_info, obj) < 0) {
        return NULL;
    }
    _buffer_info_t *old_info = stored_info;

    /* 计算信息(在简单情况下可以跳过这一步骤将会更好) */
    // 创建新的缓冲区信息对象
    info = _buffer_info_new(obj, flags);
    if (info == NULL) {
        return NULL;
    }

    // 如果存在旧信息并且新旧信息不同,则进行比较
    if (old_info != NULL && _buffer_info_cmp(info, old_info) != 0) {
        _buffer_info_t *next_info = old_info->next;
        old_info = NULL;  /* 不能使用此旧信息,但可能使用下一个 */

        // 如果 info 的维度大于1且下一个信息不为空,则比较两者
        if (info->ndim > 1 && next_info != NULL) {
            /*
             * 有些数组是C和F连续的,如果它们有多于一个维度,
             * 缓冲区信息可能在两者之间不同,因为长度为1的维度的步幅可能会调整。
             * 如果我们导出这两个缓冲区,第一个存储的可能是另一个连续性的缓冲区,
             * 因此检查两者。
             * 这在所有其他情况下通常是非常不可能的,因为在所有其他情况下,
             * 第一个将与第一个匹配,除非数组元数据被就地修改(这是不鼓励的)。
             */
            if (_buffer_info_cmp(info, next_info) == 0) {
                old_info = next_info;
            }
        }
    }
    // 如果存在旧信息,则处理新旧信息的格式相等性
    if (old_info != NULL) {
        /*
         * 如果其中一个 info->format 未设置格式(意味着格式是任意的并且可以修改)。
         * 如果新信息有格式,但我们重用旧信息,则将所有权转移到旧信息。
         */
        if (old_info->format == NULL) {
            old_info->format = info->format;
            info->format = NULL;
        }
        // 释放新信息的未标记对象并返回旧信息
        _buffer_info_free_untagged(info);
        info = old_info;
    }
    else {
        /* 将新信息插入到链接的缓冲区信息列表的第一项中 */
        info->next = stored_info;
        *buffer_info_cache_ptr = buffer_info_tag(info);
    }

    return info;
}




/*
 * 为ndarray检索缓冲区
 */
static int
array_getbuffer(PyObject *obj, Py_buffer *view, int flags)
{
    PyArrayObject *self;
    _buffer_info_t *info = NULL;
    // 定义指向 _buffer_info_t 结构体的指针变量,并初始化为 NULL

    self = (PyArrayObject*)obj;
    // 将 obj 强制转换为 PyArrayObject 类型,并赋值给 self

    /* Check whether we can provide the wanted properties */
    // 检查是否可以提供所需的属性

    if ((flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS &&
            !PyArray_CHKFLAGS(self, NPY_ARRAY_C_CONTIGUOUS)) {
        // 如果请求的是 C 连续的缓冲区,并且数组不是 C 连续的
        PyErr_SetString(PyExc_ValueError, "ndarray is not C-contiguous");
        // 设置异常,指示数组不是 C 连续的
        goto fail;
        // 跳转到失败处理部分
    }
    if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS &&
            !PyArray_CHKFLAGS(self, NPY_ARRAY_F_CONTIGUOUS)) {
        // 如果请求的是 Fortran 连续的缓冲区,并且数组不是 Fortran 连续的
        PyErr_SetString(PyExc_ValueError, "ndarray is not Fortran contiguous");
        // 设置异常,指示数组不是 Fortran 连续的
        goto fail;
        // 跳转到失败处理部分
    }
    if ((flags & PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS
            && !PyArray_ISONESEGMENT(self)) {
        // 如果请求的是任意连续的缓冲区,并且数组不是单一段的
        PyErr_SetString(PyExc_ValueError, "ndarray is not contiguous");
        // 设置异常,指示数组不是连续的
        goto fail;
        // 跳转到失败处理部分
    }
    if ((flags & PyBUF_STRIDES) != PyBUF_STRIDES &&
            !PyArray_CHKFLAGS(self, NPY_ARRAY_C_CONTIGUOUS)) {
        // 如果请求的是非步幅数组,但数组不是 C 连续的
        PyErr_SetString(PyExc_ValueError, "ndarray is not C-contiguous");
        // 设置异常,指示数组不是 C 连续的
        goto fail;
        // 跳转到失败处理部分
    }
    if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) {
        // 如果请求的缓冲区可写
        if (PyArray_FailUnlessWriteable(self, "buffer source array") < 0) {
            // 检查数组是否可写,如果不可写则设置异常
            goto fail;
            // 跳转到失败处理部分
        }
    }

    if (view == NULL) {
        // 如果传入的视图指针为空
        PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer");
        // 设置异常,表示在获取缓冲区时传入了空视图
        goto fail;
        // 跳转到失败处理部分
    }

    /* Fill in information (and add it to _buffer_info if necessary) */
    // 填充信息(如果需要,将其添加到 _buffer_info 中)
    info = _buffer_get_info(
            &((PyArrayObject_fields *)self)->_buffer_info, obj, flags);
    // 调用 _buffer_get_info 函数获取缓冲区信息,并赋值给 info
    if (info == NULL) {
        // 如果获取信息失败
        goto fail;
        // 跳转到失败处理部分
    }

    view->buf = PyArray_DATA(self);
    // 设置视图的缓冲区指针为数组的数据指针
    view->suboffsets = NULL;
    // 设置子偏移为 NULL
    view->itemsize = PyArray_ITEMSIZE(self);
    // 设置视图的每个项的大小为数组的每个项的大小

    /*
     * If a read-only buffer is requested on a read-write array, we return a
     * read-write buffer as per buffer protocol.
     * We set a requested buffer to readonly also if the array will be readonly
     * after a deprecation. This jumps the deprecation, but avoiding the
     * warning is not convenient here. A warning is given if a writeable
     * buffer is requested since `PyArray_FailUnlessWriteable` is called above
     * (and clears the `NPY_ARRAY_WARN_ON_WRITE` flag).
     */
    // 如果请求在可读写数组上获取只读缓冲区,则根据缓冲区协议返回可读写的缓冲区。
    // 如果数组在弃用后将变为只读,我们也将请求的缓冲区设置为只读。
    // 这跳过了弃用,但这里避免警告并不方便。如果请求可写的缓冲区,则会发出警告,因为上面调用了 `PyArray_FailUnlessWriteable`(并清除了 `NPY_ARRAY_WARN_ON_WRITE` 标志)。

    view->readonly = (!PyArray_ISWRITEABLE(self) ||
                      PyArray_CHKFLAGS(self, NPY_ARRAY_WARN_ON_WRITE));
    // 设置视图的只读属性,如果数组不可写或设置了 `NPY_ARRAY_WARN_ON_WRITE` 标志,则设置为只读

    view->internal = NULL;
    // 设置视图的内部指针为 NULL

    view->len = PyArray_NBYTES(self);
    // 设置视图的长度为数组的字节大小

    if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) {
        view->format = info->format;
        // 如果请求获取格式化信息,则将视图的格式指针设置为 info 中的格式信息
    } else {
        view->format = NULL;
        // 否则将格式指针设置为 NULL
    }

    if ((flags & PyBUF_ND) == PyBUF_ND) {
        view->ndim = info->ndim;
        // 如果请求获取维度信息,则将视图的维度设置为 info 中的维度信息
        view->shape = info->shape;
        // 并将形状指针设置为 info 中的形状信息
    }
    else {
        view->ndim = 0;
        // 否则将维度设置为 0
        view->shape = NULL;
        // 并将形状指针设置为 NULL
    }

    if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
        view->strides = info->strides;
        // 如果请求获取步幅信息,则将视图的步幅指针设置为 info 中的步幅信息
    }
    else {
        view->strides = NULL;
        // 否则将步幅指针设置为 NULL
    }

    view->obj = (PyObject*)self;
    // 将视图的对象指针设置为数组对象的指针

    Py_INCREF(self);
    // 增加数组对象的引用计数
    return 0;
    // 返回 0,表示获取缓冲区成功

fail:
    // 失败处理部分的标签
/*
 * 返回错误代码 -1,表示操作失败
 */
fail:
    return -1;
}

/*
 * 从 void scalar 中获取缓冲区(可以包含任意复杂类型),
 * 定义在 buffer.c 中,因为它需要复杂格式构建逻辑。
 */
NPY_NO_EXPORT int
void_getbuffer(PyObject *self, Py_buffer *view, int flags)
{
    // 将 self 强制转换为 PyVoidScalarObject 类型
    PyVoidScalarObject *scalar = (PyVoidScalarObject *)self;

    // 如果传入的 flags 包含 PyBUF_WRITABLE 标志,设置错误信息并返回 -1
    if (flags & PyBUF_WRITABLE) {
        PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly");
        return -1;
    }

    // 设置视图的维度为 0,形状为空,步长为空,子偏移为空
    view->ndim = 0;
    view->shape = NULL;
    view->strides = NULL;
    view->suboffsets = NULL;
    // 设置视图的长度和项大小为 scalar 对象描述符的元素大小
    view->len = scalar->descr->elsize;
    view->itemsize = scalar->descr->elsize;
    // 视图为只读
    view->readonly = 1;
    view->suboffsets = NULL;
    // 增加 self 的引用计数
    Py_INCREF(self);
    // 视图的对象指针指向 self
    view->obj = self;
    // 视图的缓冲区指针指向 scalar 的值
    view->buf = scalar->obval;

    // 如果 flags 不包含 PyBUF_FORMAT 标志,设置视图的格式为 NULL 并返回 0
    if (((flags & PyBUF_FORMAT) != PyBUF_FORMAT)) {
        /* It is unnecessary to find the correct format */
        view->format = NULL;
        return 0;
    }

    /*
     * 如果正在导出格式,我们需要使用 _buffer_get_info 函数
     * 来找到正确的格式。此格式也必须存储,因为理论上它可以改变
     * (实际上它不应该改变)。
     */
    // 使用 _buffer_get_info 函数获取格式信息,存储在 info 中
    _buffer_info_t *info = _buffer_get_info(&scalar->_buffer_info, self, flags);
    // 如果 info 为 NULL,释放 self 的引用计数并返回 -1
    if (info == NULL) {
        Py_DECREF(self);
        return -1;
    }
    // 设置视图的格式为 info 中的格式
    view->format = info->format;
    // 返回成功标志 0
    return 0;
}


/*************************************************************************/

// array_as_buffer 结构体,包含获取缓冲区的函数指针和释放缓冲区的函数指针
NPY_NO_EXPORT PyBufferProcs array_as_buffer = {
    (getbufferproc)array_getbuffer,
    (releasebufferproc)0,
};


/*************************************************************************
 * 将 PEP 3118 格式字符串转换为 PyArray_Descr 结构体
 */

// 快速版本的 _descriptor_from_pep3118_format 函数声明
static int
_descriptor_from_pep3118_format_fast(char const *s, PyObject **result);

// 根据字母、本地化标志和复杂性标志返回数据类型
static int
_pep3118_letter_to_type(char letter, int native, int is_complex);

// 将 PEP 3118 格式字符串转换为 PyArray_Descr 结构体的函数定义
NPY_NO_EXPORT PyArray_Descr*
_descriptor_from_pep3118_format(char const *s)
{
    char *buf, *p;
    int in_name = 0;
    int obtained;
    PyObject *descr;
    PyObject *str;
    PyObject *_numpy_internal;

    // 如果传入的 s 为 NULL,返回一个新的 NPY_BYTE 类型的 PyArray_Descr 结构体
    if (s == NULL) {
        return PyArray_DescrNewFromType(NPY_BYTE);
    }

    // 快速路径,尝试使用快速版本的 _descriptor_from_pep3118_format 函数
    obtained = _descriptor_from_pep3118_format_fast(s, &descr);
    if (obtained) {
        return (PyArray_Descr*)descr;
    }

    // 去除 s 中的空白字符,但保留字段名中的空格
    buf = malloc(strlen(s) + 1);
    if (buf == NULL) {
        PyErr_NoMemory();
        return NULL;
    }
    p = buf;
    while (*s != '\0') {
        if (*s == ':') {
            in_name = !in_name;
            *p = *s;
            p++;
        }
        else if (in_name || !NumPyOS_ascii_isspace(*s)) {
            *p = *s;
            p++;
        }
        s++;
    }
    *p = '\0';

    // 根据处理后的 buf 创建 PyUnicode 对象
    str = PyUnicode_FromStringAndSize(buf, strlen(buf));
    // 如果创建失败,释放 buf 的内存并返回 NULL
    if (str == NULL) {
        free(buf);
        return NULL;
    }

    // 导入 numpy._core._internal 模块
    _numpy_internal = PyImport_ImportModule("numpy._core._internal");
    # 如果 _numpy_internal 是 NULL,则说明未能获取到 numpy 内部对象,函数无法继续执行,返回 NULL
    if (_numpy_internal == NULL) {
        Py_DECREF(str);  # 减少字符串对象的引用计数,避免内存泄漏
        free(buf);       # 释放 buf 所占用的内存空间
        return NULL;     # 返回 NULL 表示函数执行失败
    }
    # 调用 numpy 内部对象的方法 "_dtype_from_pep3118",将 str 作为参数传递
    descr = PyObject_CallMethod(
        _numpy_internal, "_dtype_from_pep3118", "O", str);
    Py_DECREF(str);       # 减少字符串对象的引用计数,避免内存泄漏
    Py_DECREF(_numpy_internal);  # 减少 numpy 内部对象的引用计数,避免内存泄漏
    # 如果调用返回 NULL,说明处理失败,需要设置错误信息并返回 NULL
    if (descr == NULL) {
        PyObject *exc, *val, *tb;  # 定义异常、值和 traceback 对象
        PyErr_Fetch(&exc, &val, &tb);  # 获取当前的错误信息
        // 设置错误消息,指出 buf 不是一个有效的 PEP 3118 缓冲格式字符串
        PyErr_Format(PyExc_ValueError,
                     "'%s' is not a valid PEP 3118 buffer format string", buf);
        // 将当前异常链入上一个异常的原因中
        npy_PyErr_ChainExceptionsCause(exc, val, tb);
        free(buf);  # 释放 buf 所占用的内存空间
        return NULL;  # 返回 NULL 表示函数执行失败
    }
    // 检查 descr 是否是一个有效的数组描述符对象,如果不是则抛出运行时错误
    if (!PyArray_DescrCheck(descr)) {
        // 设置运行时错误信息,指出 numpy._core._internal._dtype_from_pep3118 没有返回有效的数组描述符
        PyErr_Format(PyExc_RuntimeError,
                     "internal error: numpy._core._internal._dtype_from_pep3118 "
                     "did not return a valid dtype, got %s", buf);
        Py_DECREF(descr);  // 减少描述符对象的引用计数,避免内存泄漏
        free(buf);         // 释放 buf 所占用的内存空间
        return NULL;       // 返回 NULL 表示函数执行失败
    }
    free(buf);  // 释放 buf 所占用的内存空间
    return (PyArray_Descr*)descr;  // 返回有效的数组描述符对象的指针类型
/*
 * Fast path for parsing buffer strings corresponding to simple types.
 *
 * Currently, this deals only with single-element data types.
 */



static int
_descriptor_from_pep3118_format_fast(char const *s, PyObject **result)
{
    PyArray_Descr *descr;

    int is_standard_size = 0;
    char byte_order = '=';
    int is_complex = 0;

    int type_num = NPY_BYTE;
    int item_seen = 0;

    for (; *s != '\0'; ++s) {
        is_complex = 0;
        switch (*s) {
        case '@':
        case '^':
            /* ^ means no alignment; doesn't matter for a single element */
            byte_order = '=';
            is_standard_size = 0;
            break;
        case '<':
            byte_order = '<';
            is_standard_size = 1;
            break;
        case '>':
        case '!':
            byte_order = '>';
            is_standard_size = 1;
            break;
        case '=':
            byte_order = '=';
            is_standard_size = 1;
            break;
        case 'Z':
            is_complex = 1;
            ++s;
        default:
            if (item_seen) {
                /* Not a single-element data type */
                return 0;
            }
            type_num = _pep3118_letter_to_type(*s, !is_standard_size,
                                               is_complex);
            if (type_num < 0) {
                /* Something unknown */
                return 0;
            }
            item_seen = 1;
            break;
        }
    }

    if (!item_seen) {
        return 0;
    }

    descr = PyArray_DescrFromType(type_num);
    if (descr == NULL) {
        return 0;
    }
    if (byte_order == '=') {
        *result = (PyObject*)descr;
    }
    else {
        *result = (PyObject*)PyArray_DescrNewByteorder(descr, byte_order);
        Py_DECREF(descr);
        if (*result == NULL) {
            return 0;
        }
    }

    return 1;
}



static int
_pep3118_letter_to_type(char letter, int native, int is_complex)
{
    switch (letter)
    {
    case '?': return NPY_BOOL;
    case 'b': return NPY_BYTE;
    case 'B': return NPY_UBYTE;
    case 'h': return native ? NPY_SHORT : NPY_INT16;
    case 'H': return native ? NPY_USHORT : NPY_UINT16;
    case 'i': return native ? NPY_INT : NPY_INT32;
    case 'I': return native ? NPY_UINT : NPY_UINT32;
    case 'l': return native ? NPY_LONG : NPY_INT32;
    case 'L': return native ? NPY_ULONG : NPY_UINT32;
    case 'q': return native ? NPY_LONGLONG : NPY_INT64;
    case 'Q': return native ? NPY_ULONGLONG : NPY_UINT64;
    case 'n': return native ? NPY_INTP : -1;
    case 'N': return native ? NPY_UINTP : -1;
    case 'e': return NPY_HALF;
    case 'f': return is_complex ? NPY_CFLOAT : NPY_FLOAT;
    case 'd': return is_complex ? NPY_CDOUBLE : NPY_DOUBLE;
    case 'g': return native ? (is_complex ? NPY_CLONGDOUBLE : NPY_LONGDOUBLE) : -1;
    default:
        /* Other unhandled cases */
        return -1;
    }
    return -1;
}

.\numpy\numpy\_core\src\multiarray\calculation.c

#define NPY_NO_DEPRECATED_API NPY_API_VERSION
// 定义宏,指定不使用已弃用的 NumPy API 版本

#define _MULTIARRAYMODULE
// 定义宏,指定编译器在链接时使用 multiarray 模块

#define PY_SSIZE_T_CLEAN
// 定义宏,指定 Python 的 Py_ssize_t 类型使用“clean” API

#include <Python.h>
// 包含 Python 核心头文件

#include <structmember.h>
// 包含结构成员相关的头文件

#include "numpy/arrayobject.h"
// 包含 NumPy 数组对象相关的头文件

#include "lowlevel_strided_loops.h"
// 包含低级步进循环的头文件

#include "dtypemeta.h"
// 包含数据类型元信息的头文件

#include "npy_config.h"
// 包含 NumPy 配置的头文件

#include "common.h"
// 包含通用功能的头文件

#include "number.h"
// 包含数字处理相关的头文件

#include "calculation.h"
// 包含计算相关的头文件

#include "array_assign.h"
// 包含数组赋值相关的头文件

static double
power_of_ten(int n)
{
    static const double p10[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8};
    // 静态数组,包含 10 的幂次方值

    double ret;
    // 返回值

    if (n < 9) {
        // 如果 n 小于 9,则直接返回对应 p10 数组的值
        ret = p10[n];
    }
    else {
        // 如果 n 大于等于 9,则使用循环计算 10 的 n 次方
        ret = 1e9;
        while (n-- > 9) {
            ret *= 10.;
        }
    }
    return ret;
    // 返回计算结果
}

NPY_NO_EXPORT PyObject *
_PyArray_ArgMinMaxCommon(PyArrayObject *op,
        int axis, PyArrayObject *out, int keepdims,
        npy_bool is_argmax)
{
    PyArrayObject *ap = NULL, *rp = NULL;
    // 定义 PyArrayObject 类型的指针变量 ap 和 rp

    PyArray_ArgFunc* arg_func = NULL;
    // 定义 PyArray_ArgFunc 类型的指针变量 arg_func

    char *ip, *func_name;
    // 定义字符指针变量 ip 和 func_name

    npy_intp *rptr;
    // 定义 npy_intp 类型的指针变量 rptr

    npy_intp i, n, m;
    // 定义 npy_intp 类型的变量 i, n, m

    int elsize;
    // 定义整型变量 elsize

    // 保存 axis 的副本,因为后续调用 PyArray_CheckAxis 会改变它
    int axis_copy = axis;

    npy_intp _shape_buf[NPY_MAXDIMS];
    // 定义长度为 NPY_MAXDIMS 的 npy_intp 类型数组 _shape_buf

    npy_intp *out_shape;
    // 定义 npy_intp 类型的指针变量 out_shape

    // 保存原始数组的维度数和形状,当 keepdims 为 True 时有用
    npy_intp* original_op_shape = PyArray_DIMS(op);
    int out_ndim = PyArray_NDIM(op);
    // 获取原始数组的维度数和形状

    NPY_BEGIN_THREADS_DEF;
    // 定义一个宏,用于线程控制,开始线程定义

    if ((ap = (PyArrayObject *)PyArray_CheckAxis(op, &axis, 0)) == NULL) {
        // 检查并获取经过轴处理后的数组 ap,如果失败则返回空指针
        return NULL;
    }

    /*
     * We need to permute the array so that axis is placed at the end.
     * And all other dimensions are shifted left.
     */
    // 我们需要重新排列数组,使得指定的 axis 放在最后一个位置,其他维度左移
    if (axis != PyArray_NDIM(ap)-1) {
        PyArray_Dims newaxes;
        npy_intp dims[NPY_MAXDIMS];
        int j;

        newaxes.ptr = dims;
        newaxes.len = PyArray_NDIM(ap);

        for (j = 0; j < axis; j++) {
            dims[j] = j;
        }
        for (j = axis; j < PyArray_NDIM(ap) - 1; j++) {
            dims[j] = j + 1;
        }
        dims[PyArray_NDIM(ap) - 1] = axis;

        // 对 ap 进行转置操作,得到新的数组 op
        op = (PyArrayObject *)PyArray_Transpose(ap, &newaxes);
        Py_DECREF(ap);

        if (op == NULL) {
            return NULL;
        }
    }
    else {
        op = ap;
    }

    // 获取原生字节顺序的连续副本
    PyArray_Descr *descr = NPY_DT_CALL_ensure_canonical(PyArray_DESCR(op));
    if (descr == NULL) {
        return NULL;
    }

    // 从原始数组创建一个新的数组对象 ap
    ap = (PyArrayObject *)PyArray_FromArray(op, descr, NPY_ARRAY_DEFAULT);
    Py_DECREF(op);

    if (ap == NULL) {
        return NULL;
    }

    // 决定输出数组的形状
    if (!keepdims) {
        out_ndim = PyArray_NDIM(ap) - 1;
        out_shape = PyArray_DIMS(ap);
    }
    else {
        out_shape = _shape_buf;
        // 如果需要,将输出形状初始化为形状缓冲区的内容
        if (axis_copy == NPY_RAVEL_AXIS) {
            // 如果轴复制标志为 NPY_RAVEL_AXIS,将输出形状的所有维度设为 1
            for (int i = 0; i < out_ndim; i++) {
                out_shape[i] = 1;
            }
        }
        else {
            /*
             * 虽然 `ap` 可能已经转置,但是对于 `out` 来说我们可以忽略这一点,
             * 因为转置仅重新排列大小为 1 的 `axis`(不改变内存布局)。
             */
            // 复制原始操作形状到输出形状,除了指定的轴外,其它维度保持不变
            memcpy(out_shape, original_op_shape, out_ndim * sizeof(npy_intp));
            out_shape[axis] = 1;
        }
    }

    // 如果是求取 argmax
    if (is_argmax) {
        // 设置函数名为 "argmax"
        func_name = "argmax";
        // 获取对应数据类型的 argmax 函数指针
        arg_func = PyDataType_GetArrFuncs(PyArray_DESCR(ap))->argmax;
    }
    else {
        // 设置函数名为 "argmin"
        func_name = "argmin";
        // 获取对应数据类型的 argmin 函数指针
        arg_func = PyDataType_GetArrFuncs(PyArray_DESCR(ap))->argmin;
    }
    // 如果未找到合适的函数指针,抛出类型错误异常
    if (arg_func == NULL) {
        PyErr_SetString(PyExc_TypeError,
                "data type not ordered");
        goto fail;
    }
    // 计算元素大小(以字节为单位)
    elsize = PyArray_ITEMSIZE(ap);
    // 获取数组的最后一个维度大小
    m = PyArray_DIMS(ap)[PyArray_NDIM(ap)-1];
    // 如果最后一个维度大小为 0,则抛出值错误异常
    if (m == 0) {
        PyErr_Format(PyExc_ValueError,
                    "attempt to get %s of an empty sequence",
                    func_name);
        goto fail;
    }

    // 如果输出对象为 NULL
    if (!out) {
        // 创建一个新的数组对象 rp,以 intp 类型的描述符和指定的输出形状
        rp = (PyArrayObject *)PyArray_NewFromDescr(
                Py_TYPE(ap), PyArray_DescrFromType(NPY_INTP),
                out_ndim, out_shape, NULL, NULL,
                0, (PyObject *)ap);
        // 如果创建失败,则跳转到 fail 标签处理异常
        if (rp == NULL) {
            goto fail;
        }
    }
    else {
        // 如果输出对象不为 NULL,检查其维度和形状是否与预期匹配
        if ((PyArray_NDIM(out) != out_ndim) ||
                !PyArray_CompareLists(PyArray_DIMS(out), out_shape,
                                        out_ndim)) {
            PyErr_Format(PyExc_ValueError,
                    "output array does not match result of np.%s.",
                    func_name);
            goto fail;
        }
        // 使用给定的数组对象 out 创建一个新的数组对象 rp,写入数据时若有副本则写回
        rp = (PyArrayObject *)PyArray_FromArray(out,
                              PyArray_DescrFromType(NPY_INTP),
                              NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY);
        // 如果创建失败,则跳转到 fail 标签处理异常
        if (rp == NULL) {
            goto fail;
        }
    }

    // 开始线程保护,以数组对象 ap 的描述符
    NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(ap));
    // 计算 ap 的总元素个数除以 m,得到 n 的值
    n = PyArray_SIZE(ap)/m;
    // 获取 rp 对象的数据指针
    rptr = (npy_intp *)PyArray_DATA(rp);
    // 遍历数组 ap 中的元素,每次处理 elsize*m 个元素
    for (ip = PyArray_DATA(ap), i = 0; i < n; i++, ip += elsize*m) {
        // 调用 arg_func 处理 ip 指向的数据,结果存入 rptr 指向的位置
        arg_func(ip, m, rptr, ap);
        // 移动 rptr 到下一个位置
        rptr += 1;
    }
    // 结束线程保护,以数组对象 ap 的描述符
    NPY_END_THREADS_DESCR(PyArray_DESCR(ap));

    // 释放数组对象 ap 的引用
    Py_DECREF(ap);
    /* 如果需要,触发 WRITEBACKIFCOPY */
    // 如果 out 不为 NULL 且 out 不等于 rp,则解析 WRITEBACKIFCOPY
    if (out != NULL && out != rp) {
        PyArray_ResolveWritebackIfCopy(rp);
        // 释放 rp 的引用,将其设置为 out,并增加其引用计数
        Py_DECREF(rp);
        rp = out;
        Py_INCREF(rp);
    }
    // 返回 rp 对象的 PyObject 指针形式
    return (PyObject *)rp;

 fail:
    // 处理失败的情况:释放数组对象 ap 的引用,同时释放 rp 对象的引用
    Py_DECREF(ap);
    Py_XDECREF(rp);
    // 返回 NULL 指针,表示发生异常
    return NULL;
/*NUMPY_API
 * ArgMaxWithKeepdims
 */
NPY_NO_EXPORT PyObject*
_PyArray_ArgMaxWithKeepdims(PyArrayObject *op,
        int axis, PyArrayObject *out, int keepdims)
{
    // 调用共同的最大值和最小值查找函数,返回最大值的索引,保持维度信息
    return _PyArray_ArgMinMaxCommon(op, axis, out, keepdims, 1);
}

/*NUMPY_API
 * ArgMax
 */
NPY_NO_EXPORT PyObject *
PyArray_ArgMax(PyArrayObject *op, int axis, PyArrayObject *out)
{
    // 调用共同的最大值和最小值查找函数,返回最大值的索引,不保持维度信息
    return _PyArray_ArgMinMaxCommon(op, axis, out, 0, 1);
}

/*NUMPY_API
 * ArgMinWithKeepdims
 */
NPY_NO_EXPORT PyObject *
_PyArray_ArgMinWithKeepdims(PyArrayObject *op,
        int axis, PyArrayObject *out, int keepdims)
{
    // 调用共同的最大值和最小值查找函数,返回最小值的索引,保持维度信息
    return _PyArray_ArgMinMaxCommon(op, axis, out, keepdims, 0);
}

/*NUMPY_API
 * ArgMin
 */
NPY_NO_EXPORT PyObject *
PyArray_ArgMin(PyArrayObject *op, int axis, PyArrayObject *out)
{
    // 调用共同的最大值和最小值查找函数,返回最小值的索引,不保持维度信息
    return _PyArray_ArgMinMaxCommon(op, axis, out, 0, 0);
}

/*NUMPY_API
 * Max
 */
NPY_NO_EXPORT PyObject *
PyArray_Max(PyArrayObject *ap, int axis, PyArrayObject *out)
{
    PyArrayObject *arr;
    PyObject *ret;

    // 检查并调整轴,以确保它在合法范围内
    arr = (PyArrayObject *)PyArray_CheckAxis(ap, &axis, 0);
    if (arr == NULL) {
        return NULL;
    }
    // 使用通用的规约函数找到数组中的最大值
    ret = PyArray_GenericReduceFunction(arr, n_ops.maximum, axis,
                                        PyArray_DESCR(arr)->type_num, out);
    Py_DECREF(arr);
    return ret;
}

/*NUMPY_API
 * Min
 */
NPY_NO_EXPORT PyObject *
PyArray_Min(PyArrayObject *ap, int axis, PyArrayObject *out)
{
    PyArrayObject *arr;
    PyObject *ret;

    // 检查并调整轴,以确保它在合法范围内
    arr=(PyArrayObject *)PyArray_CheckAxis(ap, &axis, 0);
    if (arr == NULL) {
        return NULL;
    }
    // 使用通用的规约函数找到数组中的最小值
    ret = PyArray_GenericReduceFunction(arr, n_ops.minimum, axis,
                                        PyArray_DESCR(arr)->type_num, out);
    Py_DECREF(arr);
    return ret;
}

/*NUMPY_API
 * Ptp (peak-to-peak)
 */
NPY_NO_EXPORT PyObject *
PyArray_Ptp(PyArrayObject *ap, int axis, PyArrayObject *out)
{
    PyArrayObject *arr;
    PyObject *ret;
    PyObject *obj1 = NULL, *obj2 = NULL;

    // 检查并调整轴,以确保它在合法范围内
    arr=(PyArrayObject *)PyArray_CheckAxis(ap, &axis, 0);
    if (arr == NULL) {
        return NULL;
    }
    // 获取数组沿指定轴的最大值
    obj1 = PyArray_Max(arr, axis, out);
    if (obj1 == NULL) {
        goto fail;
    }
    // 获取数组沿指定轴的最小值
    obj2 = PyArray_Min(arr, axis, NULL);
    if (obj2 == NULL) {
        goto fail;
    }
    Py_DECREF(arr);
    // 计算最大值和最小值的差值
    if (out) {
        ret = PyObject_CallFunction(n_ops.subtract, "OOO", out, obj2, out);
    }
    else {
        ret = PyNumber_Subtract(obj1, obj2);
    }
    Py_DECREF(obj1);
    Py_DECREF(obj2);
    return ret;

 fail:
    Py_XDECREF(arr);
    Py_XDECREF(obj1);
    Py_XDECREF(obj2);
    return NULL;
}

/*NUMPY_API
 * Std (standard deviation)
 */
NPY_NO_EXPORT PyObject *
PyArray_Std(PyArrayObject *self, int axis, int rtype, PyArrayObject *out,
            int variance)
{
    // 调用内部函数,计算数组的标准差或方差
    return __New_PyArray_Std(self, axis, rtype, out, variance, 0);
}

/* Helper function for PyArray_Std */
NPY_NO_EXPORT PyObject *
__New_PyArray_Std(PyArrayObject *self, int axis, int rtype, PyArrayObject *out,
                  int variance, int num)
{
    PyObject *obj1 = NULL, *obj2 = NULL, *obj3 = NULL;
    // 这里是实现标准差或方差计算的具体逻辑,未在这里注释
}
    // 声明指向 NumPy 数组的指针,用于存储三个数组对象及一个返回对象
    PyArrayObject *arr1 = NULL, *arr2 = NULL, *arrnew = NULL;
    PyObject *ret = NULL, *newshape = NULL;
    int i, n;
    npy_intp val;

    // 检查并返回 self 对象中指定轴的 PyArrayObject 对象
    arrnew = (PyArrayObject *)PyArray_CheckAxis(self, &axis, 0);
    if (arrnew == NULL) {
        return NULL;
    }

    /* 计算并重塑均值 */
    // 确保 arrnew 是一个数组对象,并计算其指定轴上的均值
    arr1 = (PyArrayObject *)PyArray_EnsureAnyArray(
                    PyArray_Mean(arrnew, axis, rtype, NULL));
    if (arr1 == NULL) {
        Py_DECREF(arrnew);
        return NULL;
    }

    // 获取 arrnew 的维度数,并创建一个新的元组 newshape
    n = PyArray_NDIM(arrnew);
    newshape = PyTuple_New(n);
    if (newshape == NULL) {
        Py_DECREF(arr1);
        Py_DECREF(arrnew);
        return NULL;
    }

    // 为 newshape 元组赋值,根据 arrnew 的维度设置新的形状
    for (i = 0; i < n; i++) {
        if (i == axis) {
            val = 1;
        }
        else {
            val = PyArray_DIM(arrnew,i);
        }
        PyTuple_SET_ITEM(newshape, i, PyLong_FromSsize_t(val));
    }

    // 根据 newshape 元组重塑 arr1 数组对象
    arr2 = (PyArrayObject *)PyArray_Reshape(arr1, newshape);
    Py_DECREF(arr1);
    Py_DECREF(newshape);
    if (arr2 == NULL) {
        Py_DECREF(arrnew);
        return NULL;
    }

    /* 计算 x = x - mx */
    // 确保 arrnew 和 arr2 是数组对象,计算它们的减法
    arr1 = (PyArrayObject *)PyArray_EnsureAnyArray(
                PyNumber_Subtract((PyObject *)arrnew, (PyObject *)arr2));
    Py_DECREF(arr2);
    if (arr1 == NULL) {
        Py_DECREF(arrnew);
        return NULL;
    }

    /* 计算 x * x */
    // 如果 arr1 是复数数组,取其共轭;否则直接增加其引用计数
    if (PyArray_ISCOMPLEX(arr1)) {
        obj3 = PyArray_Conjugate(arr1, NULL);
    }
    else {
        obj3 = (PyObject *)arr1;
        Py_INCREF(arr1);
    }
    if (obj3 == NULL) {
        Py_DECREF(arrnew);
        return NULL;
    }

    // 确保 arr1 和 obj3 是数组对象,使用通用的二进制函数计算它们的乘法
    arr2 = (PyArrayObject *)PyArray_EnsureAnyArray(
                PyArray_GenericBinaryFunction((PyObject *)arr1, obj3,
                                               n_ops.multiply));
    Py_DECREF(arr1);
    Py_DECREF(obj3);
    if (arr2 == NULL) {
        Py_DECREF(arrnew);
        return NULL;
    }

    // 如果 arr2 是复数数组,获取其实部;根据 rtype 设置新的数据类型
    if (PyArray_ISCOMPLEX(arr2)) {
        obj3 = PyObject_GetAttrString((PyObject *)arr2, "real");
        switch(rtype) {
        case NPY_CDOUBLE:
            rtype = NPY_DOUBLE;
            break;
        case NPY_CFLOAT:
            rtype = NPY_FLOAT;
            break;
        case NPY_CLONGDOUBLE:
            rtype = NPY_LONGDOUBLE;
            break;
        }
    }
    else {
        obj3 = (PyObject *)arr2;
        Py_INCREF(arr2);
    }
    if (obj3 == NULL) {
        Py_DECREF(arrnew);
        return NULL;
    }

    /* 计算 add.reduce(x*x,axis) */
    // 使用通用的减少函数计算 arr3(即 obj3)沿指定轴的加法
    obj1 = PyArray_GenericReduceFunction((PyArrayObject *)obj3, n_ops.add,
                                         axis, rtype, NULL);
    Py_DECREF(obj3);
    Py_DECREF(arr2);
    if (obj1 == NULL) {
        Py_DECREF(arrnew);
        return NULL;
    }

    // 获取 arrnew 在指定轴上的维度,减去 num,如果结果为 0,则设置为 1
    n = PyArray_DIM(arrnew,axis);
    Py_DECREF(arrnew);
    n = (n-num);
    if (n == 0) {
        n = 1;
    }

    // 创建一个新的 PyFloat 对象,表示计算结果的倒数
    obj2 = PyFloat_FromDouble(1.0/((double )n));
    if (obj2 == NULL) {
        Py_DECREF(obj1);
        return NULL;
    }

    // 计算最终的结果,obj1 乘以 obj2
    ret = PyNumber_Multiply(obj1, obj2);
    // 减少对象 obj1 的引用计数
    Py_DECREF(obj1);
    // 减少对象 obj2 的引用计数
    Py_DECREF(obj2);

    // 如果方差为假值(0或NULL),执行以下代码块
    if (!variance) {
        // 将返回值 ret 转换为 PyArrayObject 对象
        arr1 = (PyArrayObject *)PyArray_EnsureAnyArray(ret);
        /* 对返回值进行平方根运算 */
        ret = PyArray_GenericUnaryFunction(arr1, n_ops.sqrt);
        // 减少 arr1 对象的引用计数
        Py_DECREF(arr1);
    }

    // 如果返回值 ret 为 NULL,则返回 NULL
    if (ret == NULL) {
        return NULL;
    }

    // 如果 self 是一个确切的 PyArray 对象,跳转到 finish 标签
    if (PyArray_CheckExact(self)) {
        goto finish;
    }

    // 如果 self 是 PyArray 对象,并且 self 和 ret 的类型相同,跳转到 finish 标签
    if (PyArray_Check(self) && Py_TYPE(self) == Py_TYPE(ret)) {
        goto finish;
    }

    // 将返回值 ret 转换为 PyArrayObject 对象
    arr1 = (PyArrayObject *)PyArray_EnsureArray(ret);

    // 如果 arr1 为 NULL,则返回 NULL
    if (arr1 == NULL) {
        return NULL;
    }

    // 将 arr1 转换为一个以 self 类型为基础的视图,返回值赋给 ret
    ret = PyArray_View(arr1, NULL, Py_TYPE(self));

    // 减少 arr1 对象的引用计数
    Py_DECREF(arr1);
/*NUMPY_API
 * Round
 */
NPY_NO_EXPORT PyObject *
PyArray_Round(PyArrayObject *a, int decimals, PyArrayObject *out)
{
    PyObject *f, *ret = NULL, *tmp, *op1, *op2;
    int ret_int=0;
    PyArray_Descr *my_descr;
    // 如果提供了输出数组 out,并且其形状与输入数组 a 不匹配,则报错并返回 NULL
    if (out && (PyArray_SIZE(out) != PyArray_SIZE(a))) {
        PyErr_SetString(PyExc_ValueError,
                        "invalid output shape");
        return NULL;
    }
    // ...以下代码省略,未提供的部分不需要添加注释
}
    # 如果输入数组是复数数组,则进行以下操作
    if (PyArray_ISCOMPLEX(a)) {
        # 定义变量用于存储部分结果和数组对象
        PyObject *part;
        PyObject *round_part;
        PyObject *arr;
        int res;

        # 如果提供了输出数组对象,则使用它,增加其引用计数
        if (out) {
            arr = (PyObject *)out;
            Py_INCREF(arr);
        }
        # 否则复制输入数组a生成新的数组arr
        else {
            arr = PyArray_Copy(a);
            if (arr == NULL) {
                return NULL;
            }
        }

        /* arr.real = a.real.round(decimals) */
        # 获取复数数组a的实部,并确保其为数组对象
        part = PyObject_GetAttrString((PyObject *)a, "real");
        if (part == NULL) {
            Py_DECREF(arr);
            return NULL;
        }
        part = PyArray_EnsureAnyArray(part);
        # 对实部数组进行四舍五入操作,结果存储在round_part中
        round_part = PyArray_Round((PyArrayObject *)part,
                                   decimals, NULL);
        Py_DECREF(part);
        if (round_part == NULL) {
            Py_DECREF(arr);
            return NULL;
        }
        # 将四舍五入后的实部数组赋值给arr的实部属性
        res = PyObject_SetAttrString(arr, "real", round_part);
        Py_DECREF(round_part);
        if (res < 0) {
            Py_DECREF(arr);
            return NULL;
        }

        /* arr.imag = a.imag.round(decimals) */
        # 获取复数数组a的虚部,并确保其为数组对象
        part = PyObject_GetAttrString((PyObject *)a, "imag");
        if (part == NULL) {
            Py_DECREF(arr);
            return NULL;
        }
        part = PyArray_EnsureAnyArray(part);
        # 对虚部数组进行四舍五入操作,结果存储在round_part中
        round_part = PyArray_Round((PyArrayObject *)part,
                                   decimals, NULL);
        Py_DECREF(part);
        if (round_part == NULL) {
            Py_DECREF(arr);
            return NULL;
        }
        # 将四舍五入后的虚部数组赋值给arr的虚部属性
        res = PyObject_SetAttrString(arr, "imag", round_part);
        Py_DECREF(round_part);
        if (res < 0) {
            Py_DECREF(arr);
            return NULL;
        }
        # 返回处理后的复数数组对象arr
        return arr;
    }
    /* do the most common case first */
    # 处理最常见的情况:decimals >= 0
    if (decimals >= 0) {
        # 如果输入数组是整数数组
        if (PyArray_ISINTEGER(a)) {
            # 如果提供了输出数组对象out,则将输入数组a的内容复制到out中
            if (out) {
                if (PyArray_AssignArray(out, a,
                            NULL, NPY_DEFAULT_ASSIGN_CASTING) < 0) {
                    return NULL;
                }
                # 增加输出数组对象的引用计数并返回
                Py_INCREF(out);
                return (PyObject *)out;
            }
            else {
                # 增加输入数组a的引用计数并返回
                Py_INCREF(a);
                return (PyObject *)a;
            }
        }
        # 如果decimals为0,则调用n_ops.rint函数对输入数组a进行舍入操作
        if (decimals == 0) {
            if (out) {
                return PyObject_CallFunction(n_ops.rint, "OO", a, out);
            }
            return PyObject_CallFunction(n_ops.rint, "O", a);
        }
        # 如果decimals大于0,则指定op1为n_ops.multiply,op2为n_ops.true_divide
        op1 = n_ops.multiply;
        op2 = n_ops.true_divide;
    }
    else {
        # 如果decimals小于0,则指定op1为n_ops.true_divide,op2为n_ops.multiply,并将decimals取反
        op1 = n_ops.true_divide;
        op2 = n_ops.multiply;
        decimals = -decimals;
    }
    // 如果输出对象 out 为空,则根据输入数组 a 的类型决定如何处理
    if (!out) {
        // 如果输入数组 a 是整数类型,则设置返回整数标志并创建双精度浮点数类型描述符
        if (PyArray_ISINTEGER(a)) {
            ret_int = 1;
            my_descr = PyArray_DescrFromType(NPY_DOUBLE);
        }
        else {
            // 否则,增加输入数组 a 的描述符的引用计数,并将其作为描述符
            Py_INCREF(PyArray_DESCR(a));
            my_descr = PyArray_DESCR(a);
        }
        // 使用数组 a 的维度和描述符创建一个空的数组对象 out
        out = (PyArrayObject *)PyArray_Empty(PyArray_NDIM(a), PyArray_DIMS(a),
                                             my_descr,
                                             PyArray_ISFORTRAN(a));
        // 如果创建出错,返回空指针
        if (out == NULL) {
            return NULL;
        }
    }
    else {
        // 如果输出对象不为空,增加其引用计数
        Py_INCREF(out);
    }
    // 根据给定的小数位数创建一个 Python 浮点数对象 f
    f = PyFloat_FromDouble(power_of_ten(decimals));
    // 如果创建出错,返回空指针
    if (f == NULL) {
        return NULL;
    }
    // 调用指定函数 op1,传递数组 a、浮点数 f 和输出对象 out 作为参数
    ret = PyObject_CallFunction(op1, "OOO", a, f, out);
    // 如果调用出错,跳转至清理代码块 finish
    if (ret == NULL) {
        goto finish;
    }
    // 调用函数 n_ops.rint,传递 ret 作为参数,执行四舍五入操作
    tmp = PyObject_CallFunction(n_ops.rint, "OO", ret, ret);
    // 如果调用出错,释放 ret 并跳转至清理代码块 finish
    if (tmp == NULL) {
        Py_DECREF(ret);
        ret = NULL;
        goto finish;
    }
    // 释放 tmp 对象
    Py_DECREF(tmp);
    // 再次调用指定函数 op2,传递 ret、f 和 ret 作为参数
    tmp = PyObject_CallFunction(op2, "OOO", ret, f, ret);
    // 如果调用出错,释放 ret 并跳转至清理代码块 finish
    if (tmp == NULL) {
        Py_DECREF(ret);
        ret = NULL;
        goto finish;
    }
    // 释放 tmp 对象
    Py_DECREF(tmp);

 finish:
    // 释放浮点数对象 f
    Py_DECREF(f);
    // 释放输出对象 out
    Py_DECREF(out);
    // 如果返回整数标志为真且 ret 不为空,则将 ret 转换为输入数组 a 的类型并返回
    if (ret_int && ret != NULL) {
        Py_INCREF(PyArray_DESCR(a));
        tmp = PyArray_CastToType((PyArrayObject *)ret,
                                 PyArray_DESCR(a), PyArray_ISFORTRAN(a));
        Py_DECREF(ret);
        return tmp;
    }
    // 返回 ret 对象
    return ret;
/*NUMPY_API
 * Mean
 */
NPY_NO_EXPORT PyObject *
PyArray_Mean(PyArrayObject *self, int axis, int rtype, PyArrayObject *out)
{
    PyObject *obj1 = NULL, *obj2 = NULL, *ret;
    PyArrayObject *arr;

    // 检查并调整轴向,确保数组符合要求
    arr = (PyArrayObject *)PyArray_CheckAxis(self, &axis, 0);
    if (arr == NULL) {
        return NULL;
    }
    // 对数组进行通用约简操作,使用加法操作
    obj1 = PyArray_GenericReduceFunction(arr, n_ops.add, axis,
                                         rtype, out);
    // 创建一个包含数组指定轴向维度的浮点数对象
    obj2 = PyFloat_FromDouble((double)PyArray_DIM(arr,axis));
    Py_DECREF(arr);
    // 检查对象是否创建成功,否则清理并返回空
    if (obj1 == NULL || obj2 == NULL) {
        Py_XDECREF(obj1);
        Py_XDECREF(obj2);
        return NULL;
    }
    // 如果没有指定输出对象,则进行真实除法操作
    if (!out) {
        ret = PyNumber_TrueDivide(obj1, obj2);
    }
    // 否则,调用分裂函数进行除法操作
    else {
        ret = PyObject_CallFunction(n_ops.divide, "OOO", out, obj2, out);
    }
    Py_DECREF(obj1);
    Py_DECREF(obj2);
    return ret;
}

/*NUMPY_API
 * Any
 */
NPY_NO_EXPORT PyObject *
PyArray_Any(PyArrayObject *self, int axis, PyArrayObject *out)
{
    PyObject *arr, *ret;

    // 检查并调整轴向,确保数组符合要求
    arr = PyArray_CheckAxis(self, &axis, 0);
    if (arr == NULL) {
        return NULL;
    }
    // 对数组进行通用约简操作,使用逻辑或操作
    ret = PyArray_GenericReduceFunction((PyArrayObject *)arr,
                                        n_ops.logical_or, axis,
                                        NPY_BOOL, out);
    Py_DECREF(arr);
    return ret;
}

/*NUMPY_API
 * All
 */
NPY_NO_EXPORT PyObject *
PyArray_All(PyArrayObject *self, int axis, PyArrayObject *out)
{
    PyObject *arr, *ret;

    // 检查并调整轴向,确保数组符合要求
    arr = PyArray_CheckAxis(self, &axis, 0);
    if (arr == NULL) {
        return NULL;
    }
    // 对数组进行通用约简操作,使用逻辑与操作
    ret = PyArray_GenericReduceFunction((PyArrayObject *)arr,
                                        n_ops.logical_and, axis,
                                        NPY_BOOL, out);
    Py_DECREF(arr);
    return ret;
}


/*NUMPY_API
 * Clip
 */
NPY_NO_EXPORT PyObject *
PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *out)
{
    // 将 None 视为 NULL 处理
    if (min == Py_None) {
        min = NULL;
    }
    if (max == Py_None) {
        max = NULL;
    }

    // 必须设置 maxmin,否则报错
    if ((max == NULL) && (min == NULL)) {
        PyErr_SetString(PyExc_ValueError,
                        "array_clip: must set either max or min");
        return NULL;
    }

    // 根据条件调用对应的最小值或最大值函数,或者调用剪裁函数
    if (min == NULL) {
        return PyObject_CallFunctionObjArgs(n_ops.minimum, self, max, out, NULL);
    }
    else if (max == NULL) {
        return PyObject_CallFunctionObjArgs(n_ops.maximum, self, min, out, NULL);
    }
    else {
        return PyObject_CallFunctionObjArgs(n_ops.clip, self, min, max, out, NULL);
    }
}


/*NUMPY_API
 * Conjugate
 */
NPY_NO_EXPORT PyObject *
PyArray_Conjugate(PyArrayObject *self, PyArrayObject *out)
{
    // TO DO: Implement conjugation function
    // 尚未实现的共轭函数
    return NULL;
}
    # 检查数组是否为复数类型、对象类型或用户自定义类型
    if (PyArray_ISCOMPLEX(self) || PyArray_ISOBJECT(self) ||
            PyArray_ISUSERDEF(self)) {
        # 如果未提供输出数组(out),则调用通用的一元函数,返回其共轭
        if (out == NULL) {
            return PyArray_GenericUnaryFunction(self,
                                                n_ops.conjugate);
        }
        # 如果提供了输出数组(out),则调用通用的二元函数,将结果存储在输出数组中
        else {
            return PyArray_GenericBinaryFunction((PyObject *)self,
                                                 (PyObject *)out,
                                                 n_ops.conjugate);
        }
    }
    else {
        PyArrayObject *ret;
        # 如果数组不是数值类型,发出弃用警告
        if (!PyArray_ISNUMBER(self)) {
            /* 2017-05-04, 1.13 */
            if (DEPRECATE("attempting to conjugate non-numeric dtype; this "
                          "will error in the future to match the behavior of "
                          "np.conjugate") < 0) {
                return NULL;
            }
        }
        # 如果提供了输出数组(out),则尝试将 self 的内容复制到输出数组
        if (out) {
            # 使用默认转换方式,将 self 的内容赋值给输出数组
            if (PyArray_AssignArray(out, self,
                        NULL, NPY_DEFAULT_ASSIGN_CASTING) < 0) {
                return NULL;
            }
            # 将输出数组赋值给返回对象 ret
            ret = out;
        }
        # 如果未提供输出数组(out),则直接将 self 赋值给返回对象 ret
        else {
            ret = self;
        }
        # 增加返回对象 ret 的引用计数
        Py_INCREF(ret);
        # 返回增加引用计数后的返回对象 ret
        return (PyObject *)ret;
    }
/*NUMPY_API
 * Trace
 */
/* 定义一个不导出的函数 PyArray_Trace,接受一个 PyArrayObject 类型的参数 self,
 * 以及几个整型参数 offset, axis1, axis2, rtype 和一个 PyArrayObject 类型的参数 out。
 * 返回一个 PyObject 指针。
 */
NPY_NO_EXPORT PyObject *
PyArray_Trace(PyArrayObject *self, int offset, int axis1, int axis2,
              int rtype, PyArrayObject *out)
{
    PyObject *diag = NULL, *ret = NULL;

    /* 调用 PyArray_Diagonal 函数,传入 self, offset, axis1, axis2 参数,返回值赋给 diag */
    diag = PyArray_Diagonal(self, offset, axis1, axis2);
    /* 如果 diag 为 NULL,则直接返回 NULL */
    if (diag == NULL) {
        return NULL;
    }
    /* 调用 PyArray_GenericReduceFunction 函数,传入 diag, n_ops.add, -1, rtype, out 参数,
     * 返回值赋给 ret
     */
    ret = PyArray_GenericReduceFunction((PyArrayObject *)diag, n_ops.add, -1, rtype, out);
    /* 减少 diag 的引用计数 */
    Py_DECREF(diag);
    /* 返回 ret 指针 */
    return ret;
}

.\numpy\numpy\_core\src\multiarray\calculation.h

#ifndef NUMPY_CORE_SRC_MULTIARRAY_CALCULATION_H_
#define NUMPY_CORE_SRC_MULTIARRAY_CALCULATION_H_

// 声明不导出的函数,用于计算数组中的最大值索引,支持指定轴和输出对象
NPY_NO_EXPORT PyObject*
PyArray_ArgMax(PyArrayObject* self, int axis, PyArrayObject *out);

// 声明不导出的函数,用于计算数组中的最大值索引,支持指定轴、输出对象和保持维度标志
NPY_NO_EXPORT PyObject*
_PyArray_ArgMaxWithKeepdims(PyArrayObject* self, int axis, PyArrayObject *out, int keepdims);

// 声明不导出的函数,用于计算数组中的最小值索引,支持指定轴和输出对象
NPY_NO_EXPORT PyObject*
PyArray_ArgMin(PyArrayObject* self, int axis, PyArrayObject *out);

// 声明不导出的函数,用于计算数组中的最小值索引,支持指定轴、输出对象和保持维度标志
NPY_NO_EXPORT PyObject*
_PyArray_ArgMinWithKeepdims(PyArrayObject* self, int axis, PyArrayObject *out, int keepdims);

// 声明不导出的函数,用于计算数组中的最大值,支持指定轴和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Max(PyArrayObject* self, int axis, PyArrayObject* out);

// 声明不导出的函数,用于计算数组中的最小值,支持指定轴和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Min(PyArrayObject* self, int axis, PyArrayObject* out);

// 声明不导出的函数,用于计算数组中的峰峰值(最大值与最小值之差),支持指定轴和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Ptp(PyArrayObject* self, int axis, PyArrayObject* out);

// 声明不导出的函数,用于计算数组中的均值,支持指定轴、返回类型和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Mean(PyArrayObject* self, int axis, int rtype, PyArrayObject* out);

// 声明不导出的函数,用于对数组进行四舍五入,支持指定小数位数和输出对象
NPY_NO_EXPORT PyObject *
PyArray_Round(PyArrayObject *a, int decimals, PyArrayObject *out);

// 声明不导出的函数,用于计算数组中的迹(对角线元素之和),支持指定偏移、轴和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Trace(PyArrayObject* self, int offset, int axis1, int axis2,
                int rtype, PyArrayObject* out);

// 声明不导出的函数,用于裁剪数组,将元素限制在指定范围内,支持最小值、最大值和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Clip(PyArrayObject* self, PyObject* min, PyObject* max, PyArrayObject *out);

// 声明不导出的函数,用于对数组进行共轭操作,支持输出对象
NPY_NO_EXPORT PyObject*
PyArray_Conjugate(PyArrayObject* self, PyArrayObject* out);

// 声明不导出的函数,用于对数组进行四舍五入,支持指定小数位数和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Round(PyArrayObject* self, int decimals, PyArrayObject* out);

// 声明不导出的函数,用于计算数组中的标准差,支持指定轴、返回类型、输出对象和方差标志
NPY_NO_EXPORT PyObject*
PyArray_Std(PyArrayObject* self, int axis, int rtype, PyArrayObject* out,
                int variance);

// 声明不导出的函数,用于计算数组中的标准差,支持指定轴、返回类型、输出对象、方差标志和数值
NPY_NO_EXPORT PyObject *
__New_PyArray_Std(PyArrayObject *self, int axis, int rtype, PyArrayObject *out,
                  int variance, int num);

// 声明不导出的函数,用于计算数组中的总和,支持指定轴、返回类型和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Sum(PyArrayObject* self, int axis, int rtype, PyArrayObject* out);

// 声明不导出的函数,用于计算数组的累积和,支持指定轴、返回类型和输出对象
NPY_NO_EXPORT PyObject*
PyArray_CumSum(PyArrayObject* self, int axis, int rtype, PyArrayObject* out);

// 声明不导出的函数,用于计算数组中的乘积,支持指定轴、返回类型和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Prod(PyArrayObject* self, int axis, int rtype, PyArrayObject* out);

// 声明不导出的函数,用于计算数组的累积乘积,支持指定轴、返回类型和输出对象
NPY_NO_EXPORT PyObject*
PyArray_CumProd(PyArrayObject* self, int axis, int rtype, PyArrayObject* out);

// 声明不导出的函数,用于判断数组中所有元素是否都为真,支持指定轴和输出对象
NPY_NO_EXPORT PyObject*
PyArray_All(PyArrayObject* self, int axis, PyArrayObject* out);

// 声明不导出的函数,用于判断数组中是否有任一元素为真,支持指定轴和输出对象
NPY_NO_EXPORT PyObject*
PyArray_Any(PyArrayObject* self, int axis, PyArrayObject* out);

#endif  /* NUMPY_CORE_SRC_MULTIARRAY_CALCULATION_H_ */

.\numpy\numpy\_core\src\multiarray\can_cast_table.h

/*
 * This file defines a compile time constant casting table for use in
 * a few situations:
 * 1. As a fast-path in can-cast (untested how much it helps).
 * 2. To define the actual cast safety stored on the CastingImpl/ArrayMethod
 * 3. For scalar math, since it also needs cast safety information.
 *
 * It is useful to have this constant to allow writing compile time generic
 * code based on cast safety in the scalar math code.
 */

#ifndef NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_
#define NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_

#include "numpy/ndarraytypes.h"

/* The from type fits into to (it has a smaller or equal number of bits) */
#define FITS(FROM, TO) (NPY_SIZEOF_##FROM <= NPY_SIZEOF_##TO)
/* Unsigned "from" fits a signed integer if it is truly smaller */
#define UFITS(FROM, TO) (NPY_SIZEOF_##FROM < NPY_SIZEOF_##TO)
/* Integer "from" only fits a float if it is truly smaller or double... */
#define IFITS(FROM, TO) (  \
    NPY_SIZEOF_##FROM < NPY_SIZEOF_##TO || (  \
            NPY_SIZEOF_##FROM == NPY_SIZEOF_##TO  \
            && NPY_SIZEOF_##FROM >= NPY_SIZEOF_DOUBLE))

/*
 * NOTE: The Order is bool, integers (signed, unsigned) tuples, float, cfloat,
 *       then 6 fixed ones (object, string, unicode, void, datetime, timedelta),
 *       and finally half.
 *       Note that in the future we may only need the numeric casts here, but
 *       currently it fills in the others as well.
 */
#define CASTS_SAFELY_FROM_UINT(FROM)  \
    {0,  \
     UFITS(FROM, BYTE), FITS(FROM, BYTE), UFITS(FROM, SHORT), FITS(FROM, SHORT),  \
     UFITS(FROM, INT), FITS(FROM, INT), UFITS(FROM, LONG), FITS(FROM, LONG),  \
     UFITS(FROM, LONGLONG), FITS(FROM, LONGLONG),  \
     IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE),  \
     IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE),  \
     1, 1, 1, 1, 0, NPY_SIZEOF_##FROM < NPY_SIZEOF_TIMEDELTA, IFITS(FROM, HALF)}

#define CASTS_SAFELY_FROM_INT(FROM)  \
    {0,  \
     FITS(FROM, BYTE), 0, FITS(FROM, SHORT), 0,  \
     FITS(FROM, INT), 0, FITS(FROM, LONG), 0,  \
     FITS(FROM, LONGLONG), 0,  \
     IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE),  \
     IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE),  \
     1, 1, 1, 1, 0, NPY_SIZEOF_##FROM <= NPY_SIZEOF_TIMEDELTA, IFITS(FROM, HALF)}

/* Floats are similar to ints, but cap at double */
#define CASTS_SAFELY_FROM_FLOAT(FROM)  \
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  \
     FITS(FROM, FLOAT), FITS(FROM, DOUBLE), FITS(FROM, LONGDOUBLE),  \
     FITS(FROM, FLOAT), FITS(FROM, DOUBLE), FITS(FROM, LONGDOUBLE),  \
     1, 1, 1, 1, 0, 0, FITS(FROM, HALF)}

#define CASTS_SAFELY_FROM_CFLOAT(FROM)  \
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  \
     0, 0, 0,  \
     FITS(FROM, FLOAT), FITS(FROM, DOUBLE), FITS(FROM, LONGDOUBLE),  \
     1, 1, 1, 1, 0, 0, 0}

#endif  // NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_


注释:
/*
 * 安全类型转换表格 `_npy_can_cast_safely_table`
 * 这个表格描述了NumPy中每种数据类型之间的安全转换关系。
 * 表格的行和列代表不同的数据类型,每个元素指示从行类型到列类型的转换是否安全。
 */

static const npy_bool _npy_can_cast_safely_table[NPY_NTYPES_LEGACY][NPY_NTYPES_LEGACY] = {
        /* Bool 安全转换到除了 datetime(没有零值)之外的任何类型 */
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 0, 1, 1},
        /* 整数类型,分为有符号和无符号 */
        CASTS_SAFELY_FROM_INT(BYTE), CASTS_SAFELY_FROM_UINT(BYTE),
        CASTS_SAFELY_FROM_INT(SHORT), CASTS_SAFELY_FROM_UINT(SHORT),
        CASTS_SAFELY_FROM_INT(INT), CASTS_SAFELY_FROM_UINT(INT),
        CASTS_SAFELY_FROM_INT(LONG), CASTS_SAFELY_FROM_UINT(LONG),
        CASTS_SAFELY_FROM_INT(LONGLONG), CASTS_SAFELY_FROM_UINT(LONGLONG),
        /* 浮点数和复数 */
        CASTS_SAFELY_FROM_FLOAT(FLOAT),
        CASTS_SAFELY_FROM_FLOAT(DOUBLE),
        CASTS_SAFELY_FROM_FLOAT(LONGDOUBLE),
        CASTS_SAFELY_FROM_CFLOAT(FLOAT),
        CASTS_SAFELY_FROM_CFLOAT(DOUBLE),
        CASTS_SAFELY_FROM_CFLOAT(LONGDOUBLE),
        /*
         * 主要的数值类型后面是:
         * object, string, unicode, void, datetime, timedelta (以及 half)
         */
        /* object 类型只能安全转换到它自己 */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 0, 0, 0, 0, 0, 0},
        /* string 类型可以安全转换到 object, unicode 和 void */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 1, 1, 1, 0, 0, 0},
        /* unicode 类型可以安全转换到 object 和 void */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 0, 1, 1, 0, 0, 0},
        /* void 类型可以安全转换到 object */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 0, 0, 1, 0, 0, 0},
        /* datetime 类型可以安全转换到 object, string, unicode, void */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 1, 1, 1, 1, 0, 0},
        /* timedelta 类型可以安全转换到 object, string, unicode, void */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 1, 1, 1, 0, 1, 0},
        /* half 类型 */
        CASTS_SAFELY_FROM_FLOAT(HALF),
};

#undef FITS
#undef UFITS
#undef IFITS
#undef CASTS_SAFELY_TO_UINT
#undef CASTS_SAFELY_TO_INT
#undef CASTS_SAFELY_TO_FLOAT
#undef CASTS_SAFELY_TO_CFLOAT

#endif  /* NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_ */

.\numpy\numpy\_core\src\multiarray\common.c

/*
 * Define NPY_NO_DEPRECATED_API to use the latest NumPy API version.
 * Define _MULTIARRAYMODULE to indicate this module includes multi-array support.
 */
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE

/*
 * Ensure PY_SSIZE_T_CLEAN is defined before including Python.h
 * to use the new Py_ssize_t based API for Python objects.
 */
#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include "numpy/arrayobject.h"      // Include NumPy's array object header

#include "npy_config.h"             // Include NumPy's configuration header

#include "common.h"                 // Include common utility functions

#include "abstractdtypes.h"         // Include functions for abstract data types

#include "usertypes.h"              // Include user-defined data types

#include "npy_buffer.h"             // Include NumPy's buffer support

#include "get_attr_string.h"        // Include functions for getting attributes as strings

#include "mem_overlap.h"            // Include functions for memory overlap handling

#include "array_coercion.h"         // Include functions for array coercion


/*
 * The casting to use for implicit assignment operations resulting from
 * in-place operations (like +=) and out= arguments. (Notice that this
 * variable is misnamed, but it's part of the public API so I'm not sure we
 * can just change it. Maybe someone should try and see if anyone notices.
 */
/*
 * In numpy 1.6 and earlier, this was NPY_UNSAFE_CASTING. In a future
 * release, it will become NPY_SAME_KIND_CASTING.  Right now, during the
 * transitional period, we continue to follow the NPY_UNSAFE_CASTING rules (to
 * avoid breaking people's code), but we also check for whether the cast would
 * be allowed under the NPY_SAME_KIND_CASTING rules, and if not we issue a
 * warning (that people's code will be broken in a future release.)
 */

// Set the default casting rule for assignment operations
NPY_NO_EXPORT NPY_CASTING NPY_DEFAULT_ASSIGN_CASTING = NPY_SAME_KIND_CASTING;


// Function to find a NumPy dtype corresponding to a Python scalar object
NPY_NO_EXPORT PyArray_Descr *
_array_find_python_scalar_type(PyObject *op)
{
    // Check if the Python object is a float
    if (PyFloat_Check(op)) {
        // Return the NumPy dtype descriptor for double precision floating point
        return PyArray_DescrFromType(NPY_DOUBLE);
    }
    // Check if the Python object is a complex number
    else if (PyComplex_Check(op)) {
        // Return the NumPy dtype descriptor for double precision complex
        return PyArray_DescrFromType(NPY_CDOUBLE);
    }
    // Check if the Python object is a long integer
    else if (PyLong_Check(op)) {
        // Return the NumPy dtype descriptor discovered from the Python long object
        return NPY_DT_CALL_discover_descr_from_pyobject(
                &PyArray_PyLongDType, op);
    }
    // If the object doesn't match any of the above types, return NULL
    return NULL;
}


/*
 * Get a suitable string dtype by calling `__str__`.
 * For `np.bytes_`, this assumes an ASCII encoding.
 */
NPY_NO_EXPORT PyArray_Descr *
PyArray_DTypeFromObjectStringDiscovery(
        PyObject *obj, PyArray_Descr *last_dtype, int string_type)
{
    int itemsize;

    // If the string type is byte string (NPY_STRING)
    if (string_type == NPY_STRING) {
        // Get a string representation of the object
        PyObject *temp = PyObject_Str(obj);
        if (temp == NULL) {
            return NULL;
        }
        // Calculate the length of the string in characters
        itemsize = PyUnicode_GetLength(temp);
        Py_DECREF(temp);
        if (itemsize < 0) {
            return NULL;
        }
    }
    // If the string type is Unicode string (NPY_UNICODE)
    else if (string_type == NPY_UNICODE) {
        // Get a string representation of the object
        PyObject *temp = PyObject_Str(obj);
        if (temp == NULL) {
            return NULL;
        }
        // Calculate the length of the string in characters
        itemsize = PyUnicode_GetLength(temp);
        Py_DECREF(temp);
        if (itemsize < 0) {
            return NULL;
        }
        // Convert UCS4 codepoints to bytes (UCS4 = 4 bytes per character)
        itemsize *= 4;
    }
    else {
        // If the string type is neither NPY_STRING nor NPY_UNICODE, return NULL
        return NULL;
    }

    // If the last dtype is provided and matches the current string type and size
    if (last_dtype != NULL &&
        last_dtype->type_num == string_type &&
        last_dtype->elsize >= itemsize) {
        // Return the existing dtype with incremented reference count
        Py_INCREF(last_dtype);
        return last_dtype;
    }

    // Create a new dtype descriptor for the specified string type
    PyArray_Descr *dtype = PyArray_DescrNewFromType(string_type);
    if (dtype == NULL) {
        return NULL;
    }
    // Set the size of the dtype to match the calculated itemsize
    dtype->elsize = itemsize;
    return dtype;
}
/*
 * This function extracts the dtype from a Python object and performs shape discovery.
 * It returns only the dtype and is intended to be phased out in favor of PyArray_DiscoverDTypeAndShape.
 * This function is exported from the NumPy C API and should be used with caution.
 */
NPY_NO_EXPORT int
PyArray_DTypeFromObject(PyObject *obj, int maxdims, PyArray_Descr **out_dtype)
{
    coercion_cache_obj *cache = NULL;  // Initialize coercion cache pointer
    npy_intp shape[NPY_MAXDIMS];        // Array to store shape information
    int ndim;                           // Variable to hold number of dimensions

    // Call PyArray_DiscoverDTypeAndShape to discover dtype and shape of the object
    ndim = PyArray_DiscoverDTypeAndShape(
            obj, maxdims, shape, &cache, NULL, NULL, out_dtype, 1, NULL);
    if (ndim < 0) {
        return -1;  // Return -1 on error
    }
    npy_free_coercion_cache(cache);  // Free coercion cache memory
    return 0;  // Return 0 indicating success
}


NPY_NO_EXPORT npy_bool
_IsWriteable(PyArrayObject *ap)
{
    PyObject *base = PyArray_BASE(ap);  // Get the base object of the array
    Py_buffer view;                     // Buffer view object for array data

    /*
     * Check if the array is writable based on its base and ownership flags.
     * Arrays without a base or with owned data are considered writable.
     */
    if (base == NULL || PyArray_CHKFLAGS(ap, NPY_ARRAY_OWNDATA)) {
        /*
         * Handle cases where arrays wrapped in C-data may not own their data,
         * or where WRITEBACKIFCOPY arrays own their data but have a base.
         */
        return NPY_TRUE;  // Return true indicating array is writable
    }

    /*
     * Traverse through the base objects to find the final base.
     * If a writable array is found during traversal, return true.
     */
    while (PyArray_Check(base)) {
        ap = (PyArrayObject *)base;  // Cast base to PyArrayObject
        base = PyArray_BASE(ap);     // Update base to next base object

        if (PyArray_ISWRITEABLE(ap)) {
            /*
             * If any base is writable, return true.
             * Bases are typically collapsed to the most general one.
             */
            return NPY_TRUE;
        }

        if (base == NULL || PyArray_CHKFLAGS(ap, NPY_ARRAY_OWNDATA)) {
            /* No further base to test for writeability */
            return NPY_FALSE;
        }
        assert(!PyArray_CHKFLAGS(ap, NPY_ARRAY_OWNDATA));  // Assert ownership flag is not set
    }

    // Check if the base object supports a writable buffer view
    if (PyObject_GetBuffer(base, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) {
        PyErr_Clear();  // Clear any raised Python exceptions
        return NPY_FALSE;  // Return false if buffer view cannot be obtained
    }
    PyBuffer_Release(&view);  // Release the buffer view
    return NPY_TRUE;  // Return true indicating array is writable
}


/**
 * Convert an array shape to a string representation such as "(1, 2)".
 *
 * @param n - Dimensionality of the shape
 * @param vals - Pointer to shape array
 * @param ending - String to append after the shape "(1, 2)%s"
 *
 * @return Python unicode string object representing the shape
 */
NPY_NO_EXPORT PyObject *
convert_shape_to_string(npy_intp n, npy_intp const *vals, char *ending)
{
    npy_intp i;  // Loop variable

    /*
     * Convert the array shape into a string representation like "(1, 2)".
     * Append the specified ending string to the formatted shape.
     */

 * Convert an array shape to a string representation such as "(1, 2)".
 *
 * @param n - Dimensionality of the shape
 * @param vals - Pointer to shape array
 * @param ending - String to append after the shape "(1, 2)%s"
 *
 * @return Python unicode string object representing the shape
 */
NPY_NO_EXPORT PyObject *
convert_shape_to_string(npy_intp n, npy_intp const *vals, char *ending)
{
    npy_intp i;  // Loop variable

    /*
     * Convert the array shape into a string representation like "(1, 2)".
     * Append the specified ending string to the formatted shape.
     */
    /*
     * 如果值为负数,表示 "newaxis" 维度,对于打印来说可以丢弃,
     * 如果它是第一个维度。找到第一个非 "newaxis" 维度。
     */
    for (i = 0; i < n && vals[i] < 0; i++);

    // 如果所有维度都是 "newaxis"
    if (i == n) {
        // 返回一个格式化的空元组字符串
        return PyUnicode_FromFormat("()%s", ending);
    }

    // 创建一个字符串对象,表示第一个非 "newaxis" 维度
    PyObject *ret = PyUnicode_FromFormat("%" NPY_INTP_FMT, vals[i++]);
    if (ret == NULL) {
        return NULL;
    }

    // 处理剩余的维度
    for (; i < n; ++i) {
        PyObject *tmp;

        // 如果维度是 "newaxis"
        if (vals[i] < 0) {
            tmp = PyUnicode_FromString(",newaxis");
        }
        else {
            // 否则创建一个表示维度值的字符串对象
            tmp = PyUnicode_FromFormat(",%" NPY_INTP_FMT, vals[i]);
        }
        if (tmp == NULL) {
            Py_DECREF(ret);
            return NULL;
        }

        // 将当前维度字符串与之前的字符串连接起来
        Py_SETREF(ret, PyUnicode_Concat(ret, tmp));
        Py_DECREF(tmp);
        if (ret == NULL) {
            return NULL;
        }
    }

    // 最后格式化结果字符串,根据是否有多个维度选择不同的格式
    if (i == 1) {
        Py_SETREF(ret, PyUnicode_FromFormat("(%S,)%s", ret, ending));
    }
    else {
        Py_SETREF(ret, PyUnicode_FromFormat("(%S)%s", ret, ending));
    }
    return ret;
/**
 * dot_alignment_error - 用于报告数组形状不匹配错误的函数
 *
 * @param a: 第一个数组对象
 * @param i: 第一个数组中导致错误的维度索引
 * @param b: 第二个数组对象
 * @param j: 第二个数组中导致错误的维度索引
 */
NPY_NO_EXPORT void
dot_alignment_error(PyArrayObject *a, int i, PyArrayObject *b, int j)
{
    PyObject *errmsg = NULL, *format = NULL, *fmt_args = NULL,
             *i_obj = NULL, *j_obj = NULL,
             *shape1 = NULL, *shape2 = NULL,
             *shape1_i = NULL, *shape2_j = NULL;

    // 构建错误消息的格式字符串
    format = PyUnicode_FromString("shapes %s and %s not aligned:"
                                  " %d (dim %d) != %d (dim %d)");

    // 将数组形状转换为字符串表示
    shape1 = convert_shape_to_string(PyArray_NDIM(a), PyArray_DIMS(a), "");
    shape2 = convert_shape_to_string(PyArray_NDIM(b), PyArray_DIMS(b), "");

    // 创建表示错误的维度索引的对象
    i_obj = PyLong_FromLong(i);
    j_obj = PyLong_FromLong(j);

    // 获取导致错误的维度的大小并转换为 Python 对象
    shape1_i = PyLong_FromSsize_t(PyArray_DIM(a, i));
    shape2_j = PyLong_FromSsize_t(PyArray_DIM(b, j));

    // 如果创建任何对象失败,则跳转到 end 标签
    if (!format || !shape1 || !shape2 || !i_obj || !j_obj ||
            !shape1_i || !shape2_j) {
        goto end;
    }

    // 打包所有参数到元组中
    fmt_args = PyTuple_Pack(6, shape1, shape2,
                            shape1_i, i_obj, shape2_j, j_obj);
    if (fmt_args == NULL) {
        goto end;
    }

    // 格式化错误消息
    errmsg = PyUnicode_Format(format, fmt_args);
    if (errmsg != NULL) {
        // 设置值错误异常并附上错误消息
        PyErr_SetObject(PyExc_ValueError, errmsg);
    }
    else {
        // 如果无法格式化消息,设置通用错误消息
        PyErr_SetString(PyExc_ValueError, "shapes are not aligned");
    }

end:
    // 释放所有创建的 Python 对象
    Py_XDECREF(errmsg);
    Py_XDECREF(fmt_args);
    Py_XDECREF(format);
    Py_XDECREF(i_obj);
    Py_XDECREF(j_obj);
    Py_XDECREF(shape1);
    Py_XDECREF(shape2);
    Py_XDECREF(shape1_i);
    Py_XDECREF(shape2_j);
}

/**
 * _unpack_field - 解包 PyDataType_FIELDS(dtype) 元组
 *
 * @param value: 应为元组
 * @param descr: 将被设置为字段的数据类型描述符
 * @param offset: 将被设置为字段的偏移量
 *
 * @return: 失败返回 -1,成功返回 0
 */
NPY_NO_EXPORT int
_unpack_field(PyObject *value, PyArray_Descr **descr, npy_intp *offset)
{
    PyObject *off;

    // 检查元组长度,如果小于 2,返回错误
    if (PyTuple_GET_SIZE(value) < 2) {
        return -1;
    }

    // 设置描述符为元组的第一个元素
    *descr = (PyArray_Descr *)PyTuple_GET_ITEM(value, 0);
    // 设置偏移量为元组的第二个元素
    off = PyTuple_GET_ITEM(value, 1);

    // 如果偏移量是长整型,转换为 ssize_t 类型
    if (PyLong_Check(off)) {
        *offset = PyLong_AsSsize_t(off);
    }
    else {
        // 如果无法转换,设置索引错误异常
        PyErr_SetString(PyExc_IndexError, "can't convert offset");
        return -1;
    }

    return 0;
}

/**
 * _may_have_objects - 检查数据类型是否可能包含对象字段
 *
 * @param dtype: 要检查的数组数据类型描述符
 *
 * @return: 如果数据类型可能包含对象字段返回非零值,否则返回 0
 */
NPY_NO_EXPORT int
_may_have_objects(PyArray_Descr *dtype)
{
    PyArray_Descr *base = dtype;

    // 如果数据类型是子数组,则获取基础数据类型
    if (PyDataType_HASSUBARRAY(dtype)) {
        base = ((_PyArray_LegacyDescr *)dtype)->subarray->base;
    }

    // 检查数据类型是否有字段或者是否标记为可能包含对象
    return (PyDataType_HASFIELDS(base) ||
            PyDataType_FLAGCHK(base, NPY_ITEM_HASOBJECT));
}
/*
 * 创建一个新的空数组,尺寸为传入的大小,考虑到ap1和ap2的优先级。
 *
 * 如果`out`非空,则检查与ap1和ap2的内存重叠情况,并可能返回一个updateifcopy临时数组。
 * 如果`result`非空,则递增引用并将要返回的输出数组(如果`out`非空,则为`out`;否则为新分配的数组)放入*result。
 */
NPY_NO_EXPORT PyArrayObject *
new_array_for_sum(PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject* out,
                  int nd, npy_intp dimensions[], int typenum, PyArrayObject **result)
{
    PyArrayObject *out_buf;  // 声明PyArrayObject类型的指针out_buf

    if (out) {
        int d;

        /* 验证out是否可用 */
        if (PyArray_NDIM(out) != nd ||
            PyArray_TYPE(out) != typenum ||
            !PyArray_ISCARRAY(out)) {
            PyErr_SetString(PyExc_ValueError,
                "output array is not acceptable (must have the right datatype, "
                "number of dimensions, and be a C-Array)");
            return 0;  // 返回0,表示出错
        }
        for (d = 0; d < nd; ++d) {
            if (dimensions[d] != PyArray_DIM(out, d)) {
                PyErr_SetString(PyExc_ValueError,
                    "output array has wrong dimensions");
                return 0;  // 返回0,表示出错
            }
        }

        /* 检查内存重叠 */
        if (!(solve_may_share_memory(out, ap1, 1) == 0 &&
              solve_may_share_memory(out, ap2, 1) == 0)) {
            /* 分配临时输出数组 */
            out_buf = (PyArrayObject *)PyArray_NewLikeArray(out, NPY_CORDER,
                                                            NULL, 0);
            if (out_buf == NULL) {
                return NULL;  // 返回NULL,表示出错
            }

            /* 设置写回复制 */
            Py_INCREF(out);  // 递增引用计数
            if (PyArray_SetWritebackIfCopyBase(out_buf, out) < 0) {
                Py_DECREF(out);
                Py_DECREF(out_buf);
                return NULL;  // 返回NULL,表示出错
            }
        }
        else {
            Py_INCREF(out);  // 递增引用计数
            out_buf = out;
        }

        if (result) {
            Py_INCREF(out);  // 递增引用计数
            *result = out;  // 将out赋值给result指向的变量
        }

        return out_buf;  // 返回out_buf指向的数组对象
    }
    else {
        PyTypeObject *subtype;
        double prior1, prior2;
        /*
         * Need to choose an output array that can hold a sum
         * -- use priority to determine which subtype.
         */
        // 如果两个数组的类型不同,需要根据优先级选择一个能容纳和的输出数组类型
        if (Py_TYPE(ap2) != Py_TYPE(ap1)) {
            // 获取第二个数组的优先级
            prior2 = PyArray_GetPriority((PyObject *)ap2, 0.0);
            // 获取第一个数组的优先级
            prior1 = PyArray_GetPriority((PyObject *)ap1, 0.0);
            // 根据优先级选择子类型
            subtype = (prior2 > prior1 ? Py_TYPE(ap2) : Py_TYPE(ap1));
        }
        else {
            // 如果数组类型相同,则优先级相同,选择第一个数组的类型作为子类型
            prior1 = prior2 = 0.0;
            subtype = Py_TYPE(ap1);
        }

        // 创建新的数组对象作为输出缓冲区,使用选择的子类型
        out_buf = (PyArrayObject *)PyArray_New(subtype, nd, dimensions,
                                               typenum, NULL, NULL, 0, 0,
                                               (PyObject *)
                                               (prior2 > prior1 ? ap2 : ap1));

        // 如果成功创建输出缓冲区,并且结果指针有效,则增加输出缓冲区的引用计数,并设置结果指针
        if (out_buf != NULL && result) {
            Py_INCREF(out_buf);
            *result = out_buf;
        }

        // 返回输出缓冲区对象
        return out_buf;
    }
```cpp`
/* 检查一个 NumPy 数组是否可以转换为标量 */

NPY_NO_EXPORT int
check_is_convertible_to_scalar(PyArrayObject *v)
{
    // 如果数组的维度为 0,说明是标量,可以直接转换
    if (PyArray_NDIM(v) == 0) {
        return 0;
    }

    /* 移除此 if-else 块当不再需要支持该功能时 */
    // 如果数组大小为 1,即只有一个元素
    if (PyArray_SIZE(v) == 1) {
        /* Numpy 1.25.0, 2023-01-02 */
        // 发出弃用警告并返回 -1,表示转换不再支持
        if (DEPRECATE(
                "Conversion of an array with ndim > 0 to a scalar "
                "is deprecated, and will error in future. "
                "Ensure you extract a single element from your array "
                "before performing this operation. "
                "(Deprecated NumPy 1.25.)") < 0) {
            return -1;
        }
        return 0;
    } else {
        // 如果数组大小不为 1,则抛出类型错误异常
        PyErr_SetString(PyExc_TypeError,
            "only length-1 arrays can be converted to Python scalars");
        return -1;
    }

    // 由于之前的 return 语句会中断函数执行,因此这部分代码不会被执行到
    // 抛出类型错误异常,指示只有 0 维数组才能转换为 Python 标量
    PyErr_SetString(PyExc_TypeError,
            "only 0-dimensional arrays can be converted to Python scalars");
    return -1;
}

.\numpy\numpy\_core\src\multiarray\common.h

#ifndef NUMPY_CORE_SRC_MULTIARRAY_COMMON_H_
#define NUMPY_CORE_SRC_MULTIARRAY_COMMON_H_

#include <structmember.h>  // 导入 structmember.h 头文件
#include "numpy/npy_common.h"  // 导入 numpy 公共头文件
#include "numpy/ndarraytypes.h"  // 导入 ndarraytypes.h 头文件
#include "npy_cpu_features.h"  // 导入 npy_cpu_features.h 头文件
#include "npy_cpu_dispatch.h"  // 导入 npy_cpu_dispatch.h 头文件
#include "numpy/npy_cpu.h"  // 导入 numpy CPU 头文件

#include "npy_static_data.h"  // 导入 npy_static_data.h 头文件
#include "npy_import.h"  // 导入 npy_import.h 头文件
#include <limits.h>  // 导入 limits.h 头文件

#define error_converting(x)  (((x) == -1) && PyErr_Occurred())  // 定义宏,用于检查是否发生了错误转换并设置了异常

#ifdef NPY_ALLOW_THREADS
#define NPY_BEGIN_THREADS_NDITER(iter) \  // 如果允许多线程,定义宏以开始线程
        do { \
            if (!NpyIter_IterationNeedsAPI(iter)) { \  // 检查迭代器是否需要 API
                NPY_BEGIN_THREADS_THRESHOLDED(NpyIter_GetIterSize(iter)); \  // 根据迭代器大小设置线程阈值
            } \
        } while(0)
#else
#define NPY_BEGIN_THREADS_NDITER(iter)  // 如果不允许多线程,则为空宏
#endif

NPY_NO_EXPORT PyArray_Descr *  // 声明不导出的函数返回 PyArray_Descr 指针
PyArray_DTypeFromObjectStringDiscovery(
        PyObject *obj, PyArray_Descr *last_dtype, int string_type);  // 函数原型,从对象字符串中发现数据类型

/*
 * Recursively examines the object to determine an appropriate dtype
 * to use for converting to an ndarray.
 *
 * 'obj' is the object to be converted to an ndarray.
 *
 * 'maxdims' is the maximum recursion depth.
 *
 * 'out_dtype' should be either NULL or a minimal starting dtype when
 * the function is called. It is updated with the results of type
 * promotion. This dtype does not get updated when processing NA objects.
 *
 * Returns 0 on success, -1 on failure.
 */
NPY_NO_EXPORT int  // 声明不导出的函数返回整型
PyArray_DTypeFromObject(PyObject *obj, int maxdims,
                        PyArray_Descr **out_dtype);  // 函数原型,从对象中确定适当的数据类型

/*
 * Returns NULL without setting an exception if no scalar is matched, a
 * new dtype reference otherwise.
 */
NPY_NO_EXPORT PyArray_Descr *  // 声明不导出的函数返回 PyArray_Descr 指针
_array_find_python_scalar_type(PyObject *op);  // 函数原型,查找 Python 标量类型

NPY_NO_EXPORT npy_bool  // 声明不导出的函数返回 npy_bool 类型
_IsWriteable(PyArrayObject *ap);  // 函数原型,检查数组是否可写

NPY_NO_EXPORT PyObject *  // 声明不导出的函数返回 PyObject 指针
convert_shape_to_string(npy_intp n, npy_intp const *vals, char *ending);  // 函数原型,将形状转换为字符串

/*
 * Sets ValueError with "matrices not aligned" message for np.dot and friends
 * when a.shape[i] should match b.shape[j], but doesn't.
 */
NPY_NO_EXPORT void  // 声明不导出的函数返回空
dot_alignment_error(PyArrayObject *a, int i, PyArrayObject *b, int j);  // 函数原型,设置 "matrices not aligned" 错误消息

/**
 * unpack tuple of PyDataType_FIELDS(dtype) (descr, offset, title[not-needed])
 *
 * @param "value" should be the tuple.
 *
 * @return "descr" will be set to the field's dtype
 * @return "offset" will be set to the field's offset
 *
 * returns -1 on failure, 0 on success.
 */
NPY_NO_EXPORT int  // 声明不导出的函数返回整型
_unpack_field(PyObject *value, PyArray_Descr **descr, npy_intp *offset);  // 函数原型,解压 PyDataType_FIELDS 元组

/*
 * check whether arrays with datatype dtype might have object fields. This will
 * only happen for structured dtypes (which may have hidden objects even if the
 * HASOBJECT flag is false), object dtypes, or subarray dtypes whose base type
 * is either of these.
 */
NPY_NO_EXPORT int  // 声明不导出的函数返回整型
_may_have_objects(PyArray_Descr *dtype);  // 函数原型,检查数据类型是否可能包含对象字段

#endif  // NUMPY_CORE_SRC_MULTIARRAY_COMMON_H_
/*
 * Returns -1 and sets an exception if *index is an invalid index for
 * an array of size max_item, otherwise adjusts it in place to be
 * 0 <= *index < max_item, and returns 0.
 * 'axis' should be the array axis that is being indexed over, if known. If
 * unknown, use -1.
 * If _save is NULL it is assumed the GIL is taken
 * If _save is not NULL it is assumed the GIL is not taken and it
 * is acquired in the case of an error
 */
static inline int
check_and_adjust_index(npy_intp *index, npy_intp max_item, int axis,
                       PyThreadState * _save)
{
    /* Check that index is valid, taking into account negative indices */
    if (NPY_UNLIKELY((*index < -max_item) || (*index >= max_item))) {
        NPY_END_THREADS;  // Release the GIL before raising an exception
        /* Try to be as clear as possible about what went wrong. */
        if (axis >= 0) {
            PyErr_Format(PyExc_IndexError,
                         "index %"NPY_INTP_FMT" is out of bounds "
                         "for axis %d with size %"NPY_INTP_FMT,
                         *index, axis, max_item);  // Format error message for axis index
        } else {
            PyErr_Format(PyExc_IndexError,
                         "index %"NPY_INTP_FMT" is out of bounds "
                         "for size %"NPY_INTP_FMT, *index, max_item);  // Format error message for size index
        }
        return -1;  // Return -1 indicating error
    }
    /* adjust negative indices */
    if (*index < 0) {
        *index += max_item;  // Adjust negative index to positive
    }
    return 0;  // Return 0 indicating success
}

/*
 * Returns -1 and sets an exception if *axis is an invalid axis for
 * an array of dimension ndim, otherwise adjusts it in place to be
 * 0 <= *axis < ndim, and returns 0.
 *
 * msg_prefix: borrowed reference, a string to prepend to the message
 */
static inline int
check_and_adjust_axis_msg(int *axis, int ndim, PyObject *msg_prefix)
{
    /* Check that axis is valid, taking into account negative indices */
    if (NPY_UNLIKELY((*axis < -ndim) || (*axis >= ndim))) {
        /* Invoke the AxisError constructor */
        PyObject *exc = PyObject_CallFunction(
                npy_static_pydata.AxisError, "iiO", *axis, ndim,
                msg_prefix);  // Create AxisError exception with axis information
        if (exc == NULL) {
            return -1;  // Return -1 indicating error
        }
        PyErr_SetObject(npy_static_pydata.AxisError, exc);  // Set the created exception object
        Py_DECREF(exc);  // Decrement reference count of the exception object

        return -1;  // Return -1 indicating error
    }
    /* adjust negative indices */
    if (*axis < 0) {
        *axis += ndim;  // Adjust negative axis index to positive
    }
    return 0;  // Return 0 indicating success
}

static inline int
check_and_adjust_axis(int *axis, int ndim)
{
    return check_and_adjust_axis_msg(axis, ndim, Py_None);  // Call check_and_adjust_axis_msg with default message prefix
}

/* used for some alignment checks */
/*
 * GCC releases before GCC 4.9 had a bug in _Alignof.  See GCC bug 52023
 * <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023>.
 * clang versions < 8.0.0 have the same bug.
 */
#if (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112 \
     || (defined __GNUC__ && __GNUC__ < 4 + (__GNUC_MINOR__ < 9) \
  && !defined __clang__) \
     || (defined __clang__ && __clang_major__ < 8))
# define NPY_ALIGNOF(type) offsetof(struct {char c; type v;}, v)  // Macro definition for alignment check
#else
# define NPY_ALIGNOF(type) _Alignof(type)  // Macro definition for alignment check
#endif
#endif
#define  NPY_ALIGNOF_UINT(type) npy_uint_alignment(sizeof(type))
/*
 * 禁用无害的编译器警告 "4116: unnamed type definition in
 * parentheses",这是由 _ALIGN 宏引起的。
 */
#if defined(_MSC_VER)
#pragma warning(disable:4116)
#endif

/*
 * 如果指针对齐到 'alignment',则返回 true
 */
static inline int
npy_is_aligned(const void * p, const npy_uintp alignment)
{
    /*
     * 假设 alignment 是 2 的幂,符合 C 标准要求。
     * 假设从指针转换为 uintp 后可以进行比特位 & 运算(不是 C 标准要求,但 glibc 中使用)。
     * 这个测试比直接取模更快。
     * 注意 alignment 值为 0 是允许的,并返回 False。
     */
    return ((npy_uintp)(p) & ((alignment) - 1)) == 0;
}

/* 获取相应的 "uint" 对齐,根据 itemsize,在复制代码中使用 */
static inline npy_uintp
npy_uint_alignment(int itemsize)
{
    npy_uintp alignment = 0; /* 返回值为 0 表示不对齐 */

    switch(itemsize){
        case 1:
            return 1;
        case 2:
            alignment = NPY_ALIGNOF(npy_uint16);
            break;
        case 4:
            alignment = NPY_ALIGNOF(npy_uint32);
            break;
        case 8:
            alignment = NPY_ALIGNOF(npy_uint64);
            break;
        case 16:
            /*
             * 16 字节类型使用 2 个 uint64 赋值进行复制。
             * 参见 lowlevel_strided_loops.c 中的跨步复制函数。
             */
            alignment = NPY_ALIGNOF(npy_uint64);
            break;
        default:
            break;
    }

    return alignment;
}

/*
 * 带有步长和反转参数的 memchr
 * 适用于小搜索,其中调用 libc 的 memchr 代价高昂。
 * 步长必须是大小的倍数。
 * 与 memchr 不同,如果未找到 needle,则返回末尾的一个步长。
 */
#ifdef __clang__
    /*
     * 下面的代码当前使用 !NPY_ALIGNMENT_REQUIRED,这应该是可以的,
     * 但会导致 clang sanitizer 发出警告。可以修改代码以避免这种“非对齐”访问,
     * 但应仔细检查性能变化。
     */
    __attribute__((no_sanitize("alignment")))
#endif
static inline char *
npy_memchr(char * haystack, char needle,
           npy_intp stride, npy_intp size, npy_intp * psubloopsize, int invert)
{
    char * p = haystack;
    npy_intp subloopsize = 0;

    if (!invert) {
        /*
         * 这通常是确定要处理的元素的路径,
         * 这里性能不是很重要。
         * 如果 0 字节靠近开始,memchr 的设置成本很高。
         */
        while (subloopsize < size && *p != needle) {
            subloopsize++;
            p += stride;
        }
    }
    else {
        /* 否则情况下,处理通常是跳过路径元素 */
        
        if (!NPY_ALIGNMENT_REQUIRED && needle == 0 && stride == 1) {
            /* 如果不需要对齐且查找元素为0且步长为1时 */
            
            /* 迭代直到最后一个4的倍数 */
            char * block_end = haystack + size - (size % sizeof(unsigned int));
            while (p < block_end) {
                unsigned int  v = *(unsigned int*)p;
                if (v != 0) {
                    break;
                }
                p += sizeof(unsigned int);
            }
            
            /* 处理剩余部分 */
            subloopsize = (p - haystack);
        }
        
        while (subloopsize < size && *p == needle) {
            subloopsize++;
            p += stride;
        }
    }

    *psubloopsize = subloopsize;

    return p;
/*
 * Simple helper to create a tuple from an array of items. The `make_null_none`
 * flag means that NULL entries are replaced with None, which is occasionally
 * useful.
 */
static inline PyObject *
PyArray_TupleFromItems(int n, PyObject *const *items, int make_null_none)
{
    // 创建一个包含 n 个元素的元组
    PyObject *tuple = PyTuple_New(n);
    if (tuple == NULL) {
        return NULL;
    }
    // 遍历 items 数组,将每个元素添加到 tuple 中
    for (int i = 0; i < n; i ++) {
        PyObject *tmp;
        // 如果 make_null_none 为真且 items[i] 为 NULL,则使用 Py_None 替代
        if (!make_null_none || items[i] != NULL) {
            tmp = items[i];
        }
        else {
            tmp = Py_None;
        }
        // 增加 tmp 的引用计数,并将其设置为 tuple 的第 i 个元素
        Py_INCREF(tmp);
        PyTuple_SET_ITEM(tuple, i, tmp);
    }
    return tuple;
}

/*
 * Returns 0 if the array has rank 0, -1 otherwise. Prints a deprecation
 * warning for arrays of _size_ 1.
 */
NPY_NO_EXPORT int
check_is_convertible_to_scalar(PyArrayObject *v);


#include "ucsnarrow.h"

/*
 * Make a new empty array, of the passed size, of a type that takes the
 * priority of ap1 and ap2 into account.
 *
 * If `out` is non-NULL, memory overlap is checked with ap1 and ap2, and an
 * updateifcopy temporary array may be returned. If `result` is non-NULL, the
 * output array to be returned (`out` if non-NULL and the newly allocated array
 * otherwise) is incref'd and put to *result.
 */
NPY_NO_EXPORT PyArrayObject *
new_array_for_sum(PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject* out,
                  int nd, npy_intp dimensions[], int typenum, PyArrayObject **result);


/*
 * Used to indicate a broadcast axis, see also `npyiter_get_op_axis` in
 * `nditer_constr.c`.  This may be the preferred API for reduction axes
 * probably. So we should consider making this public either as a macro or
 * function (so that the way we flag the axis can be changed).
 */
#define NPY_ITER_REDUCTION_AXIS(axis) (axis + (1 << (NPY_BITSOF_INT - 2)))

#endif  /* NUMPY_CORE_SRC_MULTIARRAY_COMMON_H_ */