NumPy 源码解析(七十一)
.\numpy\numpy\_core\src\multiarray\refcount.h
// 声明一个不导出的函数,用于清除数组描述符指向数据的缓冲区
NPY_NO_EXPORT int
PyArray_ClearBuffer(
PyArray_Descr *descr, char *data,
npy_intp stride, npy_intp size, int aligned);
// 声明一个不导出的函数,用于清除整个数组对象
NPY_NO_EXPORT int
PyArray_ClearArray(PyArrayObject *arr);
// 声明一个不导出的函数,用于增加数组项的引用计数
NPY_NO_EXPORT void
PyArray_Item_INCREF(char *data, PyArray_Descr *descr);
// 声明一个不导出的函数,用于减少数组项的引用计数
NPY_NO_EXPORT void
PyArray_Item_XDECREF(char *data, PyArray_Descr *descr);
// 声明一个不导出的函数,用于增加数组对象的引用计数
NPY_NO_EXPORT int
PyArray_INCREF(PyArrayObject *mp);
// 声明一个不导出的函数,用于减少数组对象的引用计数
NPY_NO_EXPORT int
PyArray_XDECREF(PyArrayObject *mp);
// 声明一个不导出的函数,用于将数组对象的元素设置为None对象
NPY_NO_EXPORT int
PyArray_SetObjectsToNone(PyArrayObject *arr);
.\numpy\numpy\_core\src\multiarray\scalarapi.c
/*
定义 NPY_NO_DEPRECATED_API 为 NPY_API_VERSION,避免使用已废弃的 NumPy API 版本
定义 _MULTIARRAYMODULE,用于多维数组模块
引入必要的头文件和库
*/
// 引入其他自定义的头文件
// 定义静态函数 _descr_from_subtype,根据子类型创建 PyArray_Descr 对象
static PyArray_Descr *
_descr_from_subtype(PyObject *type)
{
PyObject *mro;
mro = ((PyTypeObject *)type)->tp_mro;
// 如果类型的方法解析顺序(MRO)长度小于 2,返回通用的对象类型描述符
if (PyTuple_GET_SIZE(mro) < 2) {
return PyArray_DescrFromType(NPY_OBJECT);
}
// 否则返回基于 MRO 的第二个项的类型描述符
return PyArray_DescrFromTypeObject(PyTuple_GET_ITEM(mro, 1));
}
// 定义 NPY_NO_EXPORT 的标量值提取函数,根据给定的描述符提取标量值
NPY_NO_EXPORT void *
scalar_value(PyObject *scalar, PyArray_Descr *descr)
{
int type_num;
int align;
uintptr_t memloc;
// 如果描述符为 NULL,根据标量对象生成描述符,并获取其类型编号
if (descr == NULL) {
descr = PyArray_DescrFromScalar(scalar);
type_num = descr->type_num;
Py_DECREF(descr);
}
else {
// 否则直接获取描述符的类型编号
type_num = descr->type_num;
}
// 根据类型编号进行不同的处理分支
switch (type_num) {
// 不同类型的标量值提取宏定义分支
CASE(BOOL, Bool);
CASE(BYTE, Byte);
CASE(UBYTE, UByte);
CASE(SHORT, Short);
CASE(USHORT, UShort);
CASE(INT, Int);
CASE(UINT, UInt);
CASE(LONG, Long);
CASE(ULONG, ULong);
CASE(LONGLONG, LongLong);
CASE(ULONGLONG, ULongLong);
CASE(HALF, Half);
CASE(FLOAT, Float);
CASE(DOUBLE, Double);
CASE(LONGDOUBLE, LongDouble);
CASE(CFLOAT, CFloat);
CASE(CDOUBLE, CDouble);
CASE(CLONGDOUBLE, CLongDouble);
CASE(OBJECT, Object);
CASE(DATETIME, Datetime);
CASE(TIMEDELTA, Timedelta);
// 对于字符串类型,返回字符串对象的指针作为标量值
case NPY_STRING:
return (void *)PyBytes_AsString(scalar);
// 对于 Unicode 类型,如果未初始化则进行延迟初始化,并返回 Unicode 数据指针
case NPY_UNICODE:
/* 懒初始化,以减少字符串标量占用的内存 */
if (PyArrayScalar_VAL(scalar, Unicode) == NULL) {
Py_UCS4 *raw_data = PyUnicode_AsUCS4Copy(scalar);
if (raw_data == NULL) {
return NULL;
}
PyArrayScalar_VAL(scalar, Unicode) = raw_data;
return (void *)raw_data;
}
return PyArrayScalar_VAL(scalar, Unicode);
// 对于 VOID 类型,直接返回 VOID 数据的指针作为标量值
case NPY_VOID:
/* 注意:这里不需要使用 &,因此不能使用 CASE 宏 */
return PyArrayScalar_VAL(scalar, Void);
}
/*
* 如果标量是用户定义类型,并且关联有(注册的)dtype,
* 那么它不能是灵活的(用户 dtype 不能是灵活的),
* 因此我们可以(而且基本上只能)假设以下逻辑始终有效。
* 即假设这个逻辑对我们的大多数类型也有效。
*/
/*
* 使用对齐标志确定在 PyObject_HEAD 之后数据从何处开始
*/
memloc = (uintptr_t)scalar;
memloc += sizeof(PyObject);
/* 现在将内存地址向最接近的对齐值进行调整 */
align = descr->alignment; // 获取描述符中的对齐值
if (align > 1) { // 如果对齐值大于1
memloc = ((memloc + align - 1) / align) * align; // 根据对齐值调整内存地址
}
return (void *)memloc; // 将调整后的内存地址转换为void指针并返回
/*NUMPY_API
* return 1 if an object is exactly a numpy scalar
*/
NPY_NO_EXPORT int
PyArray_CheckAnyScalarExact(PyObject * obj)
{
// 如果传入的对象为空,则设置错误并返回0
if (obj == NULL) {
PyErr_SetString(PyExc_ValueError,
"obj is NULL in PyArray_CheckAnyScalarExact");
return 0;
}
// 调用is_anyscalar_exact函数检查对象是否为精确的任意标量
return is_anyscalar_exact(obj);
}
/*NUMPY_API
* Convert to c-type
*
* no error checking is performed -- ctypeptr must be same type as scalar
* in case of flexible type, the data is not copied
* into ctypeptr which is expected to be a pointer to pointer
*/
NPY_NO_EXPORT void
PyArray_ScalarAsCtype(PyObject *scalar, void *ctypeptr)
{
PyArray_Descr *typecode;
void *newptr;
// 从标量对象中获取描述符
typecode = PyArray_DescrFromScalar(scalar);
// 通过标量值函数获取标量的内存地址
newptr = scalar_value(scalar, typecode);
// 如果类型码为扩展类型,ctypeptr是指针的指针,直接赋值
if (PyTypeNum_ISEXTENDED(typecode->type_num)) {
void **ct = (void **)ctypeptr;
*ct = newptr;
}
// 否则,使用memcpy将数据拷贝到ctypeptr指向的内存中
else {
memcpy(ctypeptr, newptr, typecode->elsize);
}
Py_DECREF(typecode);
return;
}
/*NUMPY_API
* Cast Scalar to c-type
*
* The output buffer must be large-enough to receive the value, this function
* should only be used for subclasses of `np.generic`, we can only guarantee
* it works for NumPy builtins.
*/
NPY_NO_EXPORT int
PyArray_CastScalarToCtype(PyObject *scalar, void *ctypeptr,
PyArray_Descr *outcode)
{
PyArray_Descr* descr;
// 获取标量对象的描述符
descr = PyArray_DescrFromScalar(scalar);
if (descr == NULL) {
return -1;
}
// 获取标量值的内存地址
void *src = scalar_value(scalar, descr);
if (src == NULL) {
Py_DECREF(descr);
return -1;
}
// 调用np_cast_raw_scalar_item进行类型转换
int res = npy_cast_raw_scalar_item(descr, src, outcode, ctypeptr);
Py_DECREF(descr);
return res;
}
/*NUMPY_API
* Cast Scalar to c-type
*/
NPY_NO_EXPORT int
PyArray_CastScalarDirect(PyObject *scalar, PyArray_Descr *indescr,
void *ctypeptr, int outtype)
{
// 获取输出数据类型的描述符
PyArray_Descr *out_dt = PyArray_DescrFromType(outtype);
if (out_dt == NULL) {
return -1;
}
// 获取输入标量值的内存地址
void *src = scalar_value(scalar, indescr);
if (src == NULL) {
Py_DECREF(out_dt);
return -1;
}
// 调用np_cast_raw_scalar_item进行直接类型转换
int res = npy_cast_raw_scalar_item(indescr, src, out_dt, ctypeptr);
Py_DECREF(out_dt);
return res;
}
/*NUMPY_API
* Get 0-dim array from scalar
*
* 0-dim array from array-scalar object
* always contains a copy of the data
* unless outcode is NULL, it is of void type and the referrer does
* not own it either.
*
* steals reference to outcode
*/
NPY_NO_EXPORT PyObject *
PyArray_FromScalar(PyObject *scalar, PyArray_Descr *outcode)
{
/* convert to 0-dim array of scalar typecode */
// 获取标量对象的类型描述符
PyArray_Descr *typecode = PyArray_DescrFromScalar(scalar);
if (typecode == NULL) {
Py_XDECREF(outcode);
return NULL;
}
// ...
}
if ((typecode->type_num == NPY_VOID) &&
!(((PyVoidScalarObject *)scalar)->flags & NPY_ARRAY_OWNDATA) &&
outcode == NULL) {
return PyArray_NewFromDescrAndBase(
&PyArray_Type, typecode,
0, NULL, NULL,
((PyVoidScalarObject *)scalar)->obval,
((PyVoidScalarObject *)scalar)->flags,
NULL, (PyObject *)scalar);
}
PyArrayObject *r = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type,
typecode,
0, NULL,
NULL, NULL, 0, NULL);
if (r == NULL) {
Py_XDECREF(outcode);
return NULL;
}
/* the dtype used by the array may be different to the one requested */
typecode = PyArray_DESCR(r);
if (PyDataType_FLAGCHK(typecode, NPY_USE_SETITEM)) {
if (PyDataType_GetArrFuncs(typecode)->setitem(scalar, PyArray_DATA(r), r) < 0) {
Py_DECREF(r);
Py_XDECREF(outcode);
return NULL;
}
}
else {
char *memptr = scalar_value(scalar, typecode);
memcpy(PyArray_DATA(r), memptr, PyArray_ITEMSIZE(r));
if (PyDataType_FLAGCHK(typecode, NPY_ITEM_HASOBJECT)) {
/* Need to INCREF just the PyObject portion */
PyArray_Item_INCREF(memptr, typecode);
}
}
if (outcode == NULL) {
return (PyObject *)r;
}
if (PyArray_EquivTypes(outcode, typecode)) {
if (!PyTypeNum_ISEXTENDED(typecode->type_num)
|| (outcode->elsize == typecode->elsize)) {
'''
* 由于类型是等价的,并且我们还没有将数组传递给任何人,
* 让我们把数据类型固定为请求的类型,即使它与传入的类型是等价的。
'''
Py_SETREF(((PyArrayObject_fields *)r)->descr, outcode);
return (PyObject *)r;
}
}
PyObject *ret = PyArray_CastToType(r, outcode, 0);
Py_DECREF(r);
return ret;
/*New reference */
/*NUMPY_API
*/
/* 从 Python 类型对象创建 NumPy 数组描述符。
如果类型是内置类型,则使用其类型编号。
返回对应的数组描述符对象或者在出错时返回 NULL。
*/
NPY_NO_EXPORT PyArray_Descr *
PyArray_DescrFromTypeObject(PyObject *type)
{
/* 如果是内置类型,获取其类型编号 */
int typenum = _typenum_fromtypeobj(type, 1);
if (typenum != NPY_NOTYPE) {
// 根据类型编号获取对应的数组描述符对象并返回
return PyArray_DescrFromType(typenum);
}
/* 检查通用的类型 */
if ((type == (PyObject *) &PyNumberArrType_Type) ||
(type == (PyObject *) &PyInexactArrType_Type) ||
(type == (PyObject *) &PyFloatingArrType_Type)) {
// 警告:将 `np.inexact` 或 `np.floating` 转换为 dtype 已不建议使用。
// 返回默认的 `float64` 数组描述符对象,不严格正确。
if (DEPRECATE("Converting `np.inexact` or `np.floating` to "
"a dtype is deprecated. The current result is `float64` "
"which is not strictly correct.") < 0) {
return NULL;
}
typenum = NPY_DOUBLE;
}
else if (type == (PyObject *) &PyComplexFloatingArrType_Type) {
// 警告:将 `np.complex` 转换为 dtype 已不建议使用。
// 返回默认的 `complex128` 数组描述符对象,不严格正确。
if (DEPRECATE("Converting `np.complex` to a dtype is deprecated. "
"The current result is `complex128` which is not "
"strictly correct.") < 0) {
return NULL;
}
typenum = NPY_CDOUBLE;
}
else if ((type == (PyObject *) &PyIntegerArrType_Type) ||
(type == (PyObject *) &PySignedIntegerArrType_Type)) {
// 警告:将 `np.integer` 或 `np.signedinteger` 转换为 dtype 已不建议使用。
// 返回默认的 `np.int_` 或者系统相关的整数类型数组描述符对象,不严格正确。
if (DEPRECATE("Converting `np.integer` or `np.signedinteger` to "
"a dtype is deprecated. The current result is "
"`np.dtype(np.int_)` which is not strictly correct. "
"Note that the result depends on the system. To ensure "
"stable results use may want to use `np.int64` or "
"`np.int32`.") < 0) {
return NULL;
}
typenum = NPY_LONG;
}
else if (type == (PyObject *) &PyUnsignedIntegerArrType_Type) {
// 警告:将 `np.unsignedinteger` 转换为 dtype 已不建议使用。
// 返回默认的 `np.uint` 或者系统相关的无符号整数类型数组描述符对象,不严格正确。
if (DEPRECATE("Converting `np.unsignedinteger` to a dtype is "
"deprecated. The current result is `np.dtype(np.uint)` "
"which is not strictly correct. Note that the result "
"depends on the system. To ensure stable results you may "
"want to use `np.uint64` or `np.uint32`.") < 0) {
return NULL;
}
typenum = NPY_ULONG;
}
else if (type == (PyObject *) &PyCharacterArrType_Type) {
// 警告:将 `np.character` 转换为 dtype 已不建议使用。
// 返回默认的 `np.str_` 或者 'S1' 类型数组描述符对象,不严格正确。
if (DEPRECATE("Converting `np.character` to a dtype is deprecated. "
"The current result is `np.dtype(np.str_)` "
"which is not strictly correct. Note that `np.character` "
"is generally deprecated and 'S1' should be used.") < 0) {
return NULL;
}
typenum = NPY_STRING;
}
/*
* 否则 --- type 是数组标量的子类型,
* 没有对应于注册的数据类型对象。
*/
else if ((type == (PyObject *) &PyGenericArrType_Type) ||
(type == (PyObject *) &PyFlexibleArrType_Type)) {
/*
* 如果是 `np.generic` 或 `np.flexible` 类型,
* 转换为 dtype 已经被弃用。
* 当前结果是 `np.dtype(np.void)`,
* 这不是严格正确的。
*/
if (DEPRECATE("Converting `np.generic` to a dtype is "
"deprecated. The current result is `np.dtype(np.void)` "
"which is not strictly correct.") < 0) {
return NULL;
}
// 设置 typenum 为 NPY_VOID
typenum = NPY_VOID;
}
// 如果 typenum 不等于 NPY_NOTYPE
if (typenum != NPY_NOTYPE) {
// 返回对应 typenum 的 PyArray_Descr 对象
return PyArray_DescrFromType(typenum);
}
/*
* 否则 --- type 是数组标量的子类型,
* 没有对应于注册的数据类型对象。
*/
/* 对于 VOID 子类型执行特殊操作 */
if (PyType_IsSubtype((PyTypeObject *)type, &PyVoidArrType_Type)) {
// 创建一个新的 _PyArray_LegacyDescr 对象,类型为 NPY_VOID
_PyArray_LegacyDescr *new = (_PyArray_LegacyDescr *)PyArray_DescrNewFromType(NPY_VOID);
if (new == NULL) {
return NULL;
}
// 尝试从 dtype 属性转换为 _PyArray_LegacyDescr 类型
_PyArray_LegacyDescr *conv = (_PyArray_LegacyDescr *)(
_arraydescr_try_convert_from_dtype_attr(type));
if (conv == NULL) {
Py_DECREF(new);
return NULL;
}
// 如果转换成功且是遗留类型,则复制字段、名称、元素大小和子数组信息
if ((PyObject *)conv != Py_NotImplemented && PyDataType_ISLEGACY(conv)) {
new->fields = conv->fields;
Py_XINCREF(new->fields);
new->names = conv->names;
Py_XINCREF(new->names);
new->elsize = conv->elsize;
new->subarray = conv->subarray;
conv->subarray = NULL;
}
Py_DECREF(conv);
Py_XDECREF(new->typeobj);
// 设置新的 _PyArray_LegacyDescr 的 typeobj 为当前 type 的 PyTypeObject 类型
new->typeobj = (PyTypeObject *)type;
Py_INCREF(type);
// 返回新的 _PyArray_LegacyDescr 类型对象
return (PyArray_Descr *)new;
}
// 否则,调用 _descr_from_subtype 函数处理
return _descr_from_subtype(type);
/*NUMPY_API
* 从数组标量对象中获取描述符对象。
*
* 返回一个新的引用。
*/
NPY_NO_EXPORT PyArray_Descr *
PyArray_DescrFromScalar(PyObject *sc)
{
int type_num; // 声明一个整数变量type_num
PyArray_Descr *descr; // 声明一个PyArray_Descr类型的指针descr,用于存储描述符对象的地址
if (PyArray_IsScalar(sc, Void)) { // 如果输入的对象是空类型的标量
descr = (PyArray_Descr *)((PyVoidScalarObject *)sc)->descr; // 获取空类型标量对象中的描述符对象
Py_INCREF(descr); // 增加描述符对象的引用计数
return descr; // 返回描述符对象
}
if (PyArray_IsScalar(sc, Datetime) || PyArray_IsScalar(sc, Timedelta)) {
PyArray_DatetimeMetaData *dt_data; // 声明一个PyArray_DatetimeMetaData类型的指针dt_data
if (PyArray_IsScalar(sc, Datetime)) {
descr = PyArray_DescrNewFromType(NPY_DATETIME); // 如果是日期时间类型的标量,创建一个日期时间描述符对象
}
else {
/* Timedelta */
descr = PyArray_DescrNewFromType(NPY_TIMEDELTA); // 如果是时间增量类型的标量,创建一个时间增量描述符对象
}
if (descr == NULL) { // 如果描述符对象创建失败
return NULL; // 返回空指针
}
dt_data = &(((PyArray_DatetimeDTypeMetaData *)((_PyArray_LegacyDescr *)descr)->c_metadata)->meta); // 获取日期时间数据元数据
memcpy(dt_data, &((PyDatetimeScalarObject *)sc)->obmeta, sizeof(PyArray_DatetimeMetaData)); // 复制标量对象中的日期时间元数据到描述符对象中
return descr; // 返回描述符对象
}
descr = PyArray_DescrFromTypeObject((PyObject *)Py_TYPE(sc)); // 从类型对象中获取描述符对象
if (descr == NULL) { // 如果获取描述符对象失败
return NULL; // 返回空指针
}
if (PyDataType_ISLEGACY(descr) && PyDataType_ISUNSIZED(descr)) { // 如果描述符对象是遗留类型且未定义大小
PyArray_DESCR_REPLACE(descr); // 替换描述符对象
if (descr == NULL) { // 如果描述符对象替换失败
return NULL; // 返回空指针
}
type_num = descr->type_num; // 获取描述符对象的类型编号
if (type_num == NPY_STRING) { // 如果是字符串类型
descr->elsize = PyBytes_GET_SIZE(sc); // 获取字节大小
}
else if (type_num == NPY_UNICODE) { // 如果是Unicode类型
descr->elsize = PyUnicode_GET_LENGTH(sc) * 4; // 获取Unicode字符的字节大小
}
else {
_PyArray_LegacyDescr *ldescr = (_PyArray_LegacyDescr *)descr; // 强制转换为遗留描述符对象类型
PyArray_Descr *dtype; // 声明一个PyArray_Descr类型的指针dtype
dtype = (PyArray_Descr *)PyObject_GetAttrString(sc, "dtype"); // 从对象中获取dtype属性
if (dtype != NULL) { // 如果获取dtype属性成功
descr->elsize = dtype->elsize; // 获取dtype对象的元素大小
ldescr->fields = PyDataType_FIELDS(dtype); // 获取字段信息
Py_XINCREF(ldescr->fields); // 增加字段信息的引用计数
ldescr->names = PyDataType_NAMES(dtype); // 获取字段名信息
Py_XINCREF(ldescr->names); // 增加字段名信息的引用计数
Py_DECREF(dtype); // 释放dtype对象的引用
}
PyErr_Clear(); // 清除异常状态
}
}
return descr; // 返回描述符对象
}
/*NUMPY_API
* 根据类型编号获取类型对象 -- 可能返回空值。
*
* 返回一个新的引用。
*/
NPY_NO_EXPORT PyObject *
PyArray_TypeObjectFromType(int type)
{
PyArray_Descr *descr; // 声明一个PyArray_Descr类型的指针descr
PyObject *obj; // 声明一个PyObject类型的指针obj
descr = PyArray_DescrFromType(type); // 根据类型编号获取描述符对象
if (descr == NULL) { // 如果获取描述符对象失败
return NULL; // 返回空指针
}
obj = (PyObject *)descr->typeobj; // 获取描述符对象的类型对象
Py_XINCREF(obj); // 增加类型对象的引用计数
Py_DECREF(descr); // 释放描述符对象的引用
return obj; // 返回类型对象
}
/* 不对descr做任何操作(不会为NULL) */
/*NUMPY_API
获取描述符描述的内存区域的标量等效对象。
*/
NPY_NO_EXPORT PyObject *
PyArray_Scalar(void *data, PyArray_Descr *descr, PyObject *base)
{
PyTypeObject *type; // 声明一个PyTypeObject类型的指针type
PyObject *obj; // 声明一个PyObject类型的指针obj
void *destptr; // 声明一个void类型的指针destptr
PyArray_CopySwapFunc *copyswap; // 声明一个PyArray_CopySwapFunc类型的指针copyswap
int type_num; // 声明一个整数变量type_num
int itemsize; // 声明一个整数变量itemsize
int swap; // 声明一个整数变量swap
type_num = descr->type_num; // 获取描述符对象的类型编号
if (type_num == NPY_BOOL) {
PyArrayScalar_RETURN_BOOL_FROM_LONG(*(npy_bool*)data);
}
else if (PyDataType_FLAGCHK(descr, NPY_USE_GETITEM)) {
return PyDataType_GetArrFuncs(descr)->getitem(data, base);
}
itemsize = descr->elsize;
copyswap = PyDataType_GetArrFuncs(descr)->copyswap;
type = descr->typeobj;
swap = !PyArray_ISNBO(descr->byteorder);
if (PyTypeNum_ISSTRING(type_num)) {
/* 消除空字符 */
char *dptr = data;
dptr += itemsize - 1;
while(itemsize && *dptr-- == 0) {
itemsize--;
}
if (type_num == NPY_UNICODE && itemsize) {
/*
* 确保字符串长度是4的倍数
* 向上舍入到最接近的倍数
*/
itemsize = (((itemsize - 1) >> 2) + 1) << 2;
}
}
if (type_num == NPY_UNICODE) {
/* 在这里需要完整的字符串长度,否则 copyswap 将写入过多的字节 */
void *buff = PyArray_malloc(descr->elsize);
if (buff == NULL) {
return PyErr_NoMemory();
}
/* copyswap 需要一个数组对象,但实际上只关心数据类型 */
PyArrayObject_fields dummy_arr;
if (base == NULL) {
dummy_arr.descr = descr;
base = (PyObject *)&dummy_arr;
}
copyswap(buff, data, swap, base);
/* 截断发生在这里 */
PyObject *u = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buff, itemsize / 4);
PyArray_free(buff);
if (u == NULL) {
return NULL;
}
PyObject *args = Py_BuildValue("(O)", u);
if (args == NULL) {
Py_DECREF(u);
return NULL;
}
obj = type->tp_new(type, args, NULL);
Py_DECREF(u);
Py_DECREF(args);
return obj;
}
if (type->tp_itemsize != 0) {
/* 字符串类型 */
obj = type->tp_alloc(type, itemsize);
}
else {
obj = type->tp_alloc(type, 0);
}
if (obj == NULL) {
return NULL;
}
if (PyTypeNum_ISDATETIME(type_num)) {
/*
* 我们需要将分辨率信息复制到标量中
* 从元数据字典中获取 void * 指针
*/
PyArray_DatetimeMetaData *dt_data;
dt_data = &(((PyArray_DatetimeDTypeMetaData *)((_PyArray_LegacyDescr *)descr)->c_metadata)->meta);
memcpy(&(((PyDatetimeScalarObject *)obj)->obmeta), dt_data,
sizeof(PyArray_DatetimeMetaData));
}
if (PyTypeNum_ISFLEXIBLE(type_num)) {
// 检查是否是灵活类型(即字符串或对象)
if (type_num == NPY_STRING) {
// 如果是字符串类型,则获取其字符数据指针
destptr = PyBytes_AS_STRING(obj);
// 在Python版本低于3.11.0b0时,重置字符串对象的哈希值
((PyBytesObject *)obj)->ob_shash = -1;
// 将数据内容复制到字符串对象中
memcpy(destptr, data, itemsize);
// 返回修改后的对象
return obj;
}
else {
// 对于非字符串类型,获取其指针对象
PyVoidScalarObject *vobj = (PyVoidScalarObject *)obj;
// 清空对象的基类和描述符,设置为传入的描述符
vobj->base = NULL;
vobj->descr = (_PyArray_LegacyDescr *)descr;
// 增加描述符的引用计数
Py_INCREF(descr);
// 清空对象的值,并设置其大小为传入的itemsize
vobj->obval = NULL;
Py_SET_SIZE(vobj, itemsize);
// 设置对象的标志为C连续存储、F顺序存储和拥有数据标志
vobj->flags = NPY_ARRAY_CARRAY | NPY_ARRAY_F_CONTIGUOUS | NPY_ARRAY_OWNDATA;
// 默认不进行数据交换
swap = 0;
// 如果描述符包含字段信息
if (PyDataType_HASFIELDS(descr)) {
// 如果有基类存在,则设置基类信息,并且不拥有数据标志位清除
if (base) {
// 增加基类的引用计数
Py_INCREF(base);
vobj->base = base;
// 获取基类的标志位
vobj->flags = PyArray_FLAGS((PyArrayObject *)base);
vobj->flags &= ~NPY_ARRAY_OWNDATA;
// 设置对象的值为传入的数据指针
vobj->obval = data;
// 返回修改后的对象
return obj;
}
}
// 如果itemsize为0,则直接返回对象
if (itemsize == 0) {
return obj;
}
// 分配新的内存空间作为对象的值
destptr = PyDataMem_NEW(itemsize);
// 如果内存分配失败,则释放对象并返回内存错误异常
if (destptr == NULL) {
Py_DECREF(obj);
return PyErr_NoMemory();
}
// 设置对象的值为新分配的内存空间
vobj->obval = destptr;
/*
* 没有基类可用于复制交换,也不需要进行交换。
* 直接将数据复制到目标中。
*/
// 如果没有提供基类,则直接复制数据到目标中
if (base == NULL) {
memcpy(destptr, data, itemsize);
// 返回修改后的对象
return obj;
}
}
}
else {
// 对于非灵活类型,使用标量值函数获取对象的值
destptr = scalar_value(obj, descr);
}
/* 对象类型为OBJECT时,复制交换操作会增加引用计数 */
// 调用复制交换函数对目标进行数据交换操作
copyswap(destptr, data, swap, base);
// 返回修改后的对象
return obj;
/* 返回数组标量,如果遇到0维数组对象 */
/* NUMPY_API
*
* 如果数组是0维且匹配Python类型,则返回数组或适当的Python对象。
* 对于0维数组,如果匹配Python类型,则释放对mp的引用。
*/
NPY_NO_EXPORT PyObject *
PyArray_Return(PyArrayObject *mp)
{
// 如果传入的数组对象为空,则返回空
if (mp == NULL) {
return NULL;
}
// 如果发生了异常,释放对mp的引用并返回空
if (PyErr_Occurred()) {
Py_XDECREF(mp);
return NULL;
}
// 如果mp不是数组对象,则直接返回mp
if (!PyArray_Check(mp)) {
return (PyObject *)mp;
}
// 如果数组是0维的
if (PyArray_NDIM(mp) == 0) {
// 将数组的数据转换为标量对象,并返回标量对象,同时释放对mp的引用
PyObject *ret;
ret = PyArray_ToScalar(PyArray_DATA(mp), mp);
Py_DECREF(mp);
return ret;
}
else {
// 如果数组不是0维的,则直接返回mp
return (PyObject *)mp;
}
}
.\numpy\numpy\_core\src\multiarray\scalartypes.h
/*
* Internal look-up tables, casting safety is defined in convert_datatype.h.
* Most of these should be phased out eventually, but some are still used.
*/
// 声明一个外部的静态数组,用于存储标量类型的种类,大小为 NPY_NTYPES_LEGACY
extern NPY_NO_EXPORT signed char
_npy_scalar_kinds_table[NPY_NTYPES_LEGACY];
// 声明一个外部的静态二维数组,用于存储类型提升规则,大小为 NPY_NTYPES_LEGACY x NPY_NTYPES_LEGACY
extern NPY_NO_EXPORT signed char
_npy_type_promotion_table[NPY_NTYPES_LEGACY][NPY_NTYPES_LEGACY];
// 声明一个外部的静态数组,用于存储每种标量类型的最小类型,大小为 NPY_NSCALARKINDS
extern NPY_NO_EXPORT signed char
_npy_smallest_type_of_kind_table[NPY_NSCALARKINDS];
// 声明一个外部的静态数组,用于存储每种类型的下一个更大的类型,大小为 NPY_NTYPES_LEGACY
extern NPY_NO_EXPORT signed char
_npy_next_larger_type_table[NPY_NTYPES_LEGACY];
// 声明一个不导出的函数,用于初始化转换表格
NPY_NO_EXPORT void
initialize_casting_tables(void);
// 声明一个不导出的函数,用于初始化数值类型
NPY_NO_EXPORT void
initialize_numeric_types(void);
// 声明一个不导出的函数,用于释放 gen-type 结构
NPY_NO_EXPORT void
gentype_struct_free(PyObject *ptr);
// 声明一个不导出的函数,用于检查是否是精确的任意标量对象
NPY_NO_EXPORT int
is_anyscalar_exact(PyObject *obj);
// 声明一个不导出的函数,根据类型对象获取类型编号
NPY_NO_EXPORT int
_typenum_fromtypeobj(PyObject *type, int user);
// 声明一个不导出的函数,用于获取标量对象的值并根据描述符转换为适当类型的指针
NPY_NO_EXPORT void *
scalar_value(PyObject *scalar, PyArray_Descr *descr);
这段代码是一个 C/C++ 的头文件,其中定义了一些静态数组和函数声明,用于管理和处理 NumPy 数组库中标量类型的内部细节和操作。
.\numpy\numpy\_core\src\multiarray\sequence.c
/* 定义以 NPY_API_VERSION 为基准的 NumPy 废弃 API 版本 */
/* 定义 _MULTIARRAYMODULE,可能用于标识多维数组模块 */
/* 清除 PY_SSIZE_T_CLEAN,确保 Py_ssize_t 被定义正确 */
/* 引入 Python.h 头文件,提供 Python C API 的核心功能 */
/* 引入 structmember.h 头文件,用于定义结构体成员和属性 */
/* 引入 NumPy 数组对象的头文件 */
/* 引入 NumPy 数组标量的头文件 */
/* 引入 NumPy 配置文件的头文件 */
/* 引入公共功能的头文件 */
/* 引入映射功能的头文件 */
/* 引入序列功能的头文件 */
/* 引入计算功能的头文件 */
/*************************************************************************
**************** 实现序列协议 Implement Sequence Protocol **************************
*************************************************************************/
/*
一些内容在 array_as_mapping 协议中也有重复。但是
我们在这里填写它,以便 PySequence_XXXX 调用按预期工作
*/
/*
检查数组是否包含指定元素 el。
相当于 (self == el).any() 的操作
*/
static int
array_contains(PyArrayObject *self, PyObject *el)
{
int ret;
PyObject *res, *any;
/* 确保将 self 和 el 作为 PyObject 比较,并返回比较结果 */
res = PyArray_EnsureAnyArray(PyObject_RichCompare((PyObject *)self,
el, Py_EQ));
if (res == NULL) {
return -1;
}
/* 对 res 应用 any 函数,检查是否存在非零元素 */
any = PyArray_Any((PyArrayObject *)res, NPY_RAVEL_AXIS, NULL);
Py_DECREF(res);
if (any == NULL) {
return -1;
}
/* 检查 any 对象是否为真 */
ret = PyObject_IsTrue(any);
Py_DECREF(any);
return ret;
}
/*
尝试对数组进行连接操作时,抛出类型错误。
注意:在 PyPy 上运行时,不会抛出此错误。
*/
static PyObject *
array_concat(PyObject *self, PyObject *other)
{
PyErr_SetString(PyExc_TypeError,
"Concatenation operation is not implemented for NumPy arrays, "
"use np.concatenate() instead. Please do not rely on this error; "
"it may not be given on all Python implementations.");
return NULL;
}
/* 定义序列协议方法 */
NPY_NO_EXPORT PySequenceMethods array_as_sequence = {
(lenfunc)array_length, /* sq_length */
(binaryfunc)array_concat, /* sq_concat for operator.concat */
(ssizeargfunc)NULL, /* sq_repeat */
(ssizeargfunc)array_item, /* sq_item */
(ssizessizeargfunc)NULL, /* sq_slice */
(ssizeobjargproc)array_assign_item, /* sq_ass_item */
(ssizessizeobjargproc)NULL, /* sq_ass_slice */
(objobjproc) array_contains, /* sq_contains */
(binaryfunc) NULL, /* sq_inplace_concat */
(ssizeargfunc)NULL /* sq_inplace_repeat */
};
.\numpy\numpy\_core\src\multiarray\sequence.h
// 定义条件编译指令,防止头文件重复包含
// 如果 NUMPY_CORE_SRC_MULTIARRAY_SEQUENCE_H_ 宏未定义,则执行下面的内容
// 声明外部链接的 PySequenceMethods 结构体变量 array_as_sequence
extern NPY_NO_EXPORT PySequenceMethods array_as_sequence;
// 结束条件编译指令块
.\numpy\numpy\_core\src\multiarray\shape.c
/* 定义 NPY_NO_DEPRECATED_API 为 NPY_API_VERSION */
/* 定义 _MULTIARRAYMODULE */
/* 定义 PY_SSIZE_T_CLEAN */
/* 包含 Python 标准头文件 */
/* 包含结构成员的头文件 */
/* 包含 NumPy 数组对象的头文件 */
/* 包含 NumPy 数组标量的头文件 */
/* 包含 NumPy 的数学函数头文件 */
/* 包含 NumPy 配置文件头文件 */
/* 包含数组包装相关的头文件 */
/* 包含构造函数相关的头文件 */
/* 包含数组形状相关的头文件 */
/* 包含 NumPy 的静态数据头文件,用于内部化字符串 */
/* 包含模板通用函数头文件,用于 npy_mul_sizes_with_overflow */
/* 包含通用函数头文件,用于 convert_shape_to_string */
/* 包含分配相关的头文件 */
/* 定义修复未知维度的静态函数 */
static int
_fix_unknown_dimension(PyArray_Dims *newshape, PyArrayObject *arr);
/* 定义尝试无拷贝重塑的静态函数 */
static int
_attempt_nocopy_reshape(PyArrayObject *self, int newnd, const npy_intp *newdims,
npy_intp *newstrides, int is_f_order);
/* 定义将零值放入指定位置的静态函数 */
static void
_putzero(char *optr, PyObject *zero, PyArray_Descr *dtype);
/* NUMPY_API
* 调整大小(重新分配数据)。仅在数组是单一段且没有其他引用的情况下才有效。
* 如果 refcheck 是 0,则不检查引用计数,假设引用计数为 1。
* 仍然需要拥有数据并且没有弱引用和基对象。
*/
NPY_NO_EXPORT PyObject *
PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck,
NPY_ORDER NPY_UNUSED(order))
{
npy_intp oldnbytes, newnbytes;
npy_intp oldsize, newsize;
int new_nd = newshape->len, k, elsize;
int refcnt;
npy_intp* new_dimensions = newshape->ptr;
npy_intp new_strides[NPY_MAXDIMS];
npy_intp *dimptr;
char *new_data;
/* 如果数组不是单一段,报错并返回空指针 */
if (!PyArray_ISONESEGMENT(self)) {
PyErr_SetString(PyExc_ValueError,
"resize only works on single-segment arrays");
return NULL;
}
/* 计算旧数组和新数组的总大小。新大小可能会溢出 */
oldsize = PyArray_SIZE(self);
newsize = 1;
for(k = 0; k < new_nd; k++) {
/* 如果维度为 0,则新大小为 0 */
if (new_dimensions[k] == 0) {
newsize = 0;
break;
}
/* 如果维度为负数,报错并返回空指针 */
if (new_dimensions[k] < 0) {
PyErr_SetString(PyExc_ValueError,
"negative dimensions not allowed");
return NULL;
}
/* 计算新大小,防止溢出 */
if (npy_mul_sizes_with_overflow(&newsize, newsize, new_dimensions[k])) {
return PyErr_NoMemory();
}
}
/* 将大小转换为字节数。新的计数可能会溢出 */
elsize = PyArray_ITEMSIZE(self);
oldnbytes = oldsize * elsize;
if (npy_mul_sizes_with_overflow(&newnbytes, newsize, elsize)) {
return PyErr_NoMemory();
}
if (oldnbytes != newnbytes) {
if (!(PyArray_FLAGS(self) & NPY_ARRAY_OWNDATA)) {
PyErr_SetString(PyExc_ValueError,
"cannot resize this array: it does not own its data");
return NULL;
}
if (PyArray_BASE(self) != NULL
|| (((PyArrayObject_fields *)self)->weakreflist != NULL)) {
PyErr_SetString(PyExc_ValueError,
"cannot resize an array that "
"references or is referenced\n"
"by another array in this way. Use the np.resize function.");
return NULL;
}
if (refcheck) {
/* 如果在 PyPy 上且 refcheck=True,则不能调整数组大小 */
PyErr_SetString(PyExc_ValueError,
"cannot resize an array with refcheck=True on PyPy.\n"
"Use the np.resize function or refcheck=False");
return NULL;
/* 否则获取对象的引用计数 */
refcnt = Py_REFCNT(self);
}
else {
/* 对于不可变对象,引用计数为 1 */
refcnt = 1;
}
if (refcnt > 2) {
/* 如果引用计数大于 2,则无法调整大小 */
PyErr_SetString(PyExc_ValueError,
"cannot resize an array that "
"references or is referenced\n"
"by another array in this way.\n"
"Use the np.resize function or refcheck=False");
return NULL;
}
/* 如果需要重新分配空间,则进行重新分配 - 分配 0 大小被禁止 */
PyObject *handler = PyArray_HANDLER(self);
if (handler == NULL) {
/* 如果没有找到内存处理器但设置了 NPY_ARRAY_OWNDATA 标志,则报错 */
PyErr_SetString(PyExc_RuntimeError,
"no memory handler found but OWNDATA flag set");
return NULL;
}
/* 使用 PyDataMem_UserRENEW 重新分配数据 */
new_data = PyDataMem_UserRENEW(PyArray_DATA(self),
newnbytes == 0 ? elsize : newnbytes,
handler);
if (new_data == NULL) {
/* 如果无法分配内存,则报内存错误 */
PyErr_SetString(PyExc_MemoryError,
"cannot allocate memory for array");
return NULL;
}
/* 更新数组对象的数据指针 */
((PyArrayObject_fields *)self)->data = new_data;
}
/* 如果新分配的大小大于旧大小且数组可写,则填充新分配的内存区域为零 */
if (newnbytes > oldnbytes && PyArray_ISWRITEABLE(self)) {
/* 如果数组的数据类型包含引用计数,则使用 _putzero 函数填充 */
if (PyDataType_FLAGCHK(PyArray_DESCR(self), NPY_ITEM_REFCOUNT)) {
PyObject *zero = PyLong_FromLong(0);
char *optr;
/* 指向新内存的起始位置 */
optr = PyArray_BYTES(self) + oldnbytes;
/* 计算需要填充零的元素数量 */
npy_intp n_new = newsize - oldsize;
/* 循环填充零 */
for (npy_intp i = 0; i < n_new; i++) {
_putzero((char *)optr, zero, PyArray_DESCR(self));
optr += elsize;
}
/* 释放零对象 */
Py_DECREF(zero);
}
else {
/* 否则使用 memset 函数填充零 */
memset(PyArray_BYTES(self) + oldnbytes, 0, newnbytes - oldnbytes);
}
}
if (new_nd > 0) {
if (PyArray_NDIM(self) != new_nd) {
/* 不同的维度数。*/
((PyArrayObject_fields *)self)->nd = new_nd;
/* 需要新的维度和步长数组 */
dimptr = PyDimMem_RENEW(PyArray_DIMS(self), 3*new_nd);
if (dimptr == NULL) {
PyErr_SetString(PyExc_MemoryError,
"cannot allocate memory for array");
return NULL;
}
((PyArrayObject_fields *)self)->dimensions = dimptr;
((PyArrayObject_fields *)self)->strides = dimptr + new_nd;
}
/* 生成新的步长变量 */
_array_fill_strides(new_strides, new_dimensions, new_nd,
PyArray_ITEMSIZE(self), PyArray_FLAGS(self),
&(((PyArrayObject_fields *)self)->flags));
memmove(PyArray_DIMS(self), new_dimensions, new_nd*sizeof(npy_intp));
memmove(PyArray_STRIDES(self), new_strides, new_nd*sizeof(npy_intp));
}
else {
PyDimMem_FREE(((PyArrayObject_fields *)self)->dimensions);
((PyArrayObject_fields *)self)->nd = 0;
((PyArrayObject_fields *)self)->dimensions = NULL;
((PyArrayObject_fields *)self)->strides = NULL;
}
Py_RETURN_NONE;
/*
* 返回一个新的数组
* 根据旧数组的数据和顺序参数创建具有新形状的数组
* 只在必要时复制数据
*/
/*NUMPY_API
* 为数组创建新形状
*/
NPY_NO_EXPORT PyObject *
PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
NPY_ORDER order)
{
// 调用内部函数进行重塑,根据需要复制数据
return _reshape_with_copy_arg(self, newdims, order, NPY_COPY_IF_NEEDED);
}
NPY_NO_EXPORT PyObject *
_reshape_with_copy_arg(PyArrayObject *array, PyArray_Dims *newdims,
NPY_ORDER order, NPY_COPYMODE copy)
{
npy_intp i;
npy_intp *dimensions = newdims->ptr; // 新形状的维度数组
PyArrayObject *ret; // 返回的重塑后的数组对象
int ndim = newdims->len; // 新形状的维度数
npy_bool same; // 标志变量,表示新旧形状是否相同
npy_intp *strides = NULL; // 步长数组,默认为空
npy_intp newstrides[NPY_MAXDIMS]; // 新数组的步长数组
int flags; // 标志变量
// 如果顺序为 NPY_ANYORDER,则根据数组是否为 Fortran 风格或 C 风格决定顺序
if (order == NPY_ANYORDER) {
order = PyArray_ISFORTRAN(array) ? NPY_FORTRANORDER : NPY_CORDER;
}
// 如果顺序为 NPY_KEEPORDER,不允许使用 'K' 来进行重塑,抛出异常
else if (order == NPY_KEEPORDER) {
PyErr_SetString(PyExc_ValueError,
"order 'K' is not permitted for reshaping");
return NULL;
}
// 快速检查是否需要执行重塑操作
if (ndim == PyArray_NDIM(array) && copy != NPY_COPY_ALWAYS) {
same = NPY_TRUE;
i = 0;
while (same && i < ndim) {
// 检查每个维度是否相同
if (PyArray_DIM(array, i) != dimensions[i]) {
same = NPY_FALSE;
}
i++;
}
// 如果形状相同,则返回数组的视图
if (same) {
return PyArray_View(array, NULL, NULL);
}
}
/*
* 修正任何 -1 的维度,并检查新形状与旧数组大小的匹配情况
*/
if (_fix_unknown_dimension(newdims, array) < 0) {
return NULL;
}
/*
* 内存顺序不依赖于复制/非复制的上下文
* 始终根据 'order' 参数决定顺序
*/
// 如果需要始终复制数据,则创建数组的副本
if (copy == NPY_COPY_ALWAYS) {
PyObject *newcopy = PyArray_NewCopy(array, order);
if (newcopy == NULL) {
return NULL;
}
array = (PyArrayObject *)newcopy;
}
else {
/*
* 有时我们必须创建数组的新副本
* 以便获取正确的方向和
* 因为我们不能简单地重用带有
* 数据顺序的缓冲区。
*/
Py_INCREF(array); // 增加数组的引用计数,以防止其被意外释放
if (((order == NPY_CORDER && !PyArray_IS_C_CONTIGUOUS(array)) ||
(order == NPY_FORTRANORDER && !PyArray_IS_F_CONTIGUOUS(array)))) {
int success = 0;
success = _attempt_nocopy_reshape(array, ndim, dimensions,
newstrides, order);
if (success) {
/* 原地重塑成功,不需要复制数组 */
strides = newstrides; // 更新步幅为新计算的步幅
}
else if (copy == NPY_COPY_NEVER) {
PyErr_SetString(PyExc_ValueError,
"Unable to avoid creating a copy while reshaping.");
Py_DECREF(array); // 减少数组的引用计数
return NULL; // 返回空指针表示错误
}
else {
PyObject *newcopy = PyArray_NewCopy(array, order);
Py_DECREF(array); // 减少原始数组的引用计数
if (newcopy == NULL) {
return NULL; // 如果复制失败,返回空指针
}
array = (PyArrayObject *)newcopy; // 更新数组为新复制的数组
}
}
}
/* 我们总是必须正确地解释连续的缓冲区 */
/* 确保 flags 参数被设置。*/
flags = PyArray_FLAGS(array); // 获取数组的标志位
if (ndim > 1) {
if (order == NPY_FORTRANORDER) {
flags &= ~NPY_ARRAY_C_CONTIGUOUS; // 清除 C 连续标志位
flags |= NPY_ARRAY_F_CONTIGUOUS; // 设置 Fortran 连续标志位
}
else {
flags &= ~NPY_ARRAY_F_CONTIGUOUS; // 清除 Fortran 连续标志位
flags |= NPY_ARRAY_C_CONTIGUOUS; // 设置 C 连续标志位
}
}
Py_INCREF(PyArray_DESCR(array)); // 增加数组描述符的引用计数
ret = (PyArrayObject *)PyArray_NewFromDescr_int(
Py_TYPE(array), PyArray_DESCR(array),
ndim, dimensions, strides, PyArray_DATA(array),
flags, (PyObject *)array, (PyObject *)array,
_NPY_ARRAY_ENSURE_DTYPE_IDENTITY);
Py_DECREF(array); // 减少数组的引用计数
return (PyObject *)ret; // 返回处理后的 Python 对象
/* For backward compatibility -- Not recommended */
/*NUMPY_API
* Reshape
*/
/* 定义 PyArray_Reshape 函数,用于对数组进行重新形状化操作 */
NPY_NO_EXPORT PyObject *
PyArray_Reshape(PyArrayObject *self, PyObject *shape)
{
PyObject *ret;
PyArray_Dims newdims;
/* 使用 PyArray_IntpConverter 将 shape 转换为 PyArray_Dims 结构 */
if (!PyArray_IntpConverter(shape, &newdims)) {
return NULL;
}
/* 调用 PyArray_Newshape 函数重新构造数组的形状 */
ret = PyArray_Newshape(self, &newdims, NPY_CORDER);
/* 释放 newdims 占用的内存 */
npy_free_cache_dim_obj(newdims);
return ret;
}
/* 定义 _putzero 函数,用于在数组中置零操作 */
static void
_putzero(char *optr, PyObject *zero, PyArray_Descr *dtype)
{
/* 如果 dtype 不是引用计数类型,则使用 memset 将 optr 置零 */
if (!PyDataType_FLAGCHK(dtype, NPY_ITEM_REFCOUNT)) {
memset(optr, 0, dtype->elsize);
}
/* 如果 dtype 有字段 */
else if (PyDataType_HASFIELDS(dtype)) {
PyObject *key, *value, *title = NULL;
PyArray_Descr *new;
int offset;
Py_ssize_t pos = 0;
/* 遍历 dtype 的字段 */
while (PyDict_Next(PyDataType_FIELDS(dtype), &pos, &key, &value)) {
/* 如果字段是标题字段,则跳过 */
if (NPY_TITLE_KEY(key, value)) {
continue;
}
/* 解析字段的信息 */
if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) {
return;
}
/* 在 optr + offset 处置零 */
_putzero(optr + offset, zero, new);
}
}
/* 否则按 dtype 的元素大小置零 */
else {
npy_intp i;
npy_intp nsize = dtype->elsize / sizeof(zero);
/* 遍历元素并置零 */
for (i = 0; i < nsize; i++) {
Py_INCREF(zero);
memcpy(optr, &zero, sizeof(zero));
optr += sizeof(zero);
}
}
return;
}
/*
* attempt to reshape an array without copying data
*
* The requested newdims are not checked, but must be compatible with
* the size of self, which must be non-zero. Other than that this
* function should correctly handle all reshapes, including axes of
* length 1. Zero strides should work but are untested.
*
* If a copy is needed, returns 0
* If no copy is needed, returns 1 and fills newstrides
* with appropriate strides
*
* The "is_f_order" argument describes how the array should be viewed
* during the reshape, not how it is stored in memory (that
* information is in PyArray_STRIDES(self)).
*
* If some output dimensions have length 1, the strides assigned to
* them are arbitrary. In the current implementation, they are the
* stride of the next-fastest index.
*/
/* 定义 _attempt_nocopy_reshape 函数,尝试在不复制数据的情况下重新形状化数组 */
static int
_attempt_nocopy_reshape(PyArrayObject *self, int newnd, const npy_intp *newdims,
npy_intp *newstrides, int is_f_order)
{
int oldnd;
npy_intp olddims[NPY_MAXDIMS];
npy_intp oldstrides[NPY_MAXDIMS];
npy_intp last_stride;
int oi, oj, ok, ni, nj, nk;
oldnd = 0;
/*
* Remove axes with dimension 1 from the old array. They have no effect
* but would need special cases since their strides do not matter.
*/
/* 从旧数组中移除维度为 1 的轴 */
for (oi = 0; oi < PyArray_NDIM(self); oi++) {
if (PyArray_DIMS(self)[oi]!= 1) {
olddims[oldnd] = PyArray_DIMS(self)[oi];
oldstrides[oldnd] = PyArray_STRIDES(self)[oi];
oldnd++;
}
}
/* oi to oj and ni to nj give the axis ranges currently worked with */
oi = 0;
oj = 1;
ni = 0;
nj = 1;
while (ni < newnd && oi < oldnd) {
npy_intp np = newdims[ni]; // 获取新维度数组中的当前维度大小
npy_intp op = olddims[oi]; // 获取旧维度数组中的当前维度大小
while (np != op) {
if (np < op) {
/* Misses trailing 1s, these are handled later */
np *= newdims[nj++]; // 如果新维度小于旧维度,则将当前维度乘以下一个新维度以处理后续的尾随1
} else {
op *= olddims[oj++]; // 否则,将当前维度乘以下一个旧维度
}
}
/* Check whether the original axes can be combined */
for (ok = oi; ok < oj - 1; ok++) {
if (is_f_order) {
if (oldstrides[ok+1] != olddims[ok]*oldstrides[ok]) {
/* not contiguous enough */
return 0; // 如果不是足够连续的,返回0
}
}
else {
/* C order */
if (oldstrides[ok] != olddims[ok+1]*oldstrides[ok+1]) {
/* not contiguous enough */
return 0; // 如果不是足够连续的,返回0
}
}
}
/* Calculate new strides for all axes currently worked with */
if (is_f_order) {
newstrides[ni] = oldstrides[oi]; // 如果是Fortran顺序,则将新步幅设置为旧步幅
for (nk = ni + 1; nk < nj; nk++) {
newstrides[nk] = newstrides[nk - 1]*newdims[nk - 1]; // 对于后续的轴,根据前一个轴的步幅和维度计算新步幅
}
}
else {
/* C order */
newstrides[nj - 1] = oldstrides[oj - 1]; // 如果是C顺序,则将最后一个新步幅设置为最后一个旧步幅
for (nk = nj - 1; nk > ni; nk--) {
newstrides[nk - 1] = newstrides[nk]*newdims[nk]; // 对于前面的轴,根据后一个轴的步幅和维度计算新步幅
}
}
ni = nj++; // 更新新维度索引并增加下一个新维度索引
oi = oj++; // 更新旧维度索引并增加下一个旧维度索引
}
/*
* Set strides corresponding to trailing 1s of the new shape.
*/
if (ni >= 1) {
last_stride = newstrides[ni - 1]; // 如果新维度索引大于等于1,设置最后一个步幅为前一个新步幅
}
else {
last_stride = PyArray_ITEMSIZE(self); // 否则,设置最后一个步幅为数组中的每个元素的大小
}
if (is_f_order) {
last_stride *= newdims[ni - 1]; // 如果是Fortran顺序,根据最后一个新维度调整最后一个步幅
}
for (nk = ni; nk < newnd; nk++) {
newstrides[nk] = last_stride; // 设置新维度中剩余轴的步幅为最后一个步幅
}
return 1; // 返回成功
}
static void
raise_reshape_size_mismatch(PyArray_Dims *newshape, PyArrayObject *arr)
{
// 将新形状转换为字符串表示
PyObject *tmp = convert_shape_to_string(newshape->len, newshape->ptr, "");
// 如果转换成功
if (tmp != NULL) {
// 抛出格式化的 ValueError 异常
PyErr_Format(PyExc_ValueError,
"cannot reshape array of size %zd into shape %S",
PyArray_SIZE(arr), tmp);
// 释放临时对象
Py_DECREF(tmp);
}
}
static int
_fix_unknown_dimension(PyArray_Dims *newshape, PyArrayObject *arr)
{
npy_intp *dimensions;
npy_intp s_original = PyArray_SIZE(arr);
npy_intp i_unknown, s_known;
int i, n;
dimensions = newshape->ptr;
n = newshape->len;
s_known = 1;
i_unknown = -1;
// 遍历新形状的维度数组
for (i = 0; i < n; i++) {
// 处理未知维度的情况
if (dimensions[i] < 0) {
// 如果已经有未知维度
if (i_unknown == -1) {
i_unknown = i;
}
else {
// 抛出值错误异常,只能指定一个未知维度
PyErr_SetString(PyExc_ValueError,
"can only specify one unknown dimension");
return -1;
}
}
else if (npy_mul_sizes_with_overflow(&s_known, s_known,
dimensions[i])) {
// 如果计算维度乘积溢出,抛出重塑大小不匹配异常
raise_reshape_size_mismatch(newshape, arr);
return -1;
}
}
// 处理存在未知维度的情况
if (i_unknown >= 0) {
// 如果已知维度为 0 或者原始数组大小不能整除已知维度
if (s_known == 0 || s_original % s_known != 0) {
// 抛出重塑大小不匹配异常
raise_reshape_size_mismatch(newshape, arr);
return -1;
}
// 计算未知维度的值
dimensions[i_unknown] = s_original / s_known;
}
else {
// 如果没有未知维度,检查原始大小是否等于已知维度
if (s_original != s_known) {
// 抛出重塑大小不匹配异常
raise_reshape_size_mismatch(newshape, arr);
return -1;
}
}
return 0;
}
/*NUMPY_API
*
* return a new view of the array object with all of its unit-length
* dimensions squeezed out if needed, otherwise
* return the same array.
*/
NPY_NO_EXPORT PyObject *
PyArray_Squeeze(PyArrayObject *self)
{
PyArrayObject *ret;
npy_bool unit_dims[NPY_MAXDIMS];
int idim, ndim, any_ones;
npy_intp *shape;
// 获取数组的维度数和形状
ndim = PyArray_NDIM(self);
shape = PyArray_SHAPE(self);
any_ones = 0;
// 遍历数组的每个维度
for (idim = 0; idim < ndim; ++idim) {
// 如果维度为 1,则表示为单位维度
if (shape[idim] == 1) {
unit_dims[idim] = 1;
any_ones = 1;
}
else {
unit_dims[idim] = 0;
}
}
/* 如果没有单位维度需要挤压,直接返回原数组 */
if (!any_ones) {
// 增加原数组的引用计数并返回
Py_INCREF(self);
return (PyObject *)self;
}
// 创建数组的视图,去除单位维度
ret = (PyArrayObject *)PyArray_View(self, NULL, &PyArray_Type);
// 如果创建视图失败,返回空指针
if (ret == NULL) {
return NULL;
}
// 在视图中移除指定的单位维度
PyArray_RemoveAxesInPlace(ret, unit_dims);
/*
* 如果 self 不是基类 ndarray,调用其 __array_wrap__ 方法
*/
if (Py_TYPE(self) != &PyArray_Type) {
// 应用简单的数组包装方法,并返回包装后的对象
PyObject *wrapped = npy_apply_wrap_simple(self, ret);
Py_DECREF(ret);
return wrapped;
}
// 返回视图对象
return (PyObject *)ret;
}
/*
* Just like PyArray_Squeeze, but allows the caller to select
* a subset of the size-one dimensions to squeeze out.
*/
/*
* Squeeze selected axes in a NumPy array.
*/
NPY_NO_EXPORT PyObject *
PyArray_SqueezeSelected(PyArrayObject *self, npy_bool *axis_flags)
{
PyArrayObject *ret;
int idim, ndim, any_ones;
npy_intp *shape;
ndim = PyArray_NDIM(self); // 获取数组的维度数
shape = PyArray_SHAPE(self); // 获取数组的形状
/* Verify that the axes requested are all of size one */
any_ones = 0;
for (idim = 0; idim < ndim; ++idim) {
if (axis_flags[idim] != 0) { // 如果axis_flags[idim]为真
if (shape[idim] == 1) { // 如果该轴的大小为1
any_ones = 1; // 设置标志位,表示存在可以压缩的轴
}
else {
PyErr_SetString(PyExc_ValueError,
"cannot select an axis to squeeze out "
"which has size not equal to one"); // 抛出值错误异常,说明无法压缩大小不为1的轴
return NULL; // 返回空指针
}
}
}
/* If there were no axes to squeeze out, return the same array */
if (!any_ones) { // 如果没有需要压缩的轴
Py_INCREF(self); // 增加对self的引用计数
return (PyObject *)self; // 返回self数组的Python对象指针
}
ret = (PyArrayObject *)PyArray_View(self, NULL, &PyArray_Type); // 创建一个self的视图
if (ret == NULL) { // 如果视图创建失败
return NULL; // 返回空指针
}
PyArray_RemoveAxesInPlace(ret, axis_flags); // 在ret中就地移除指定的轴
/*
* If self isn't not a base class ndarray, call its
* __array_wrap__ method
*/
if (Py_TYPE(self) != &PyArray_Type) { // 如果self不是基类ndarray
PyObject *wrapped = npy_apply_wrap_simple(self, ret); // 调用self的__array_wrap__方法
Py_DECREF(ret); // 减少对ret的引用计数
return wrapped; // 返回调用结果
}
return (PyObject *)ret; // 返回视图ret的Python对象指针
}
/*
* SwapAxes in a NumPy array.
*/
/*NUMPY_API
* SwapAxes
*/
NPY_NO_EXPORT PyObject *
PyArray_SwapAxes(PyArrayObject *ap, int a1, int a2)
{
PyArray_Dims new_axes;
npy_intp dims[NPY_MAXDIMS];
int n = PyArray_NDIM(ap);
int i;
if (check_and_adjust_axis_msg(&a1, n, npy_interned_str.axis1) < 0) { // 检查并调整轴a1
return NULL; // 返回空指针
}
if (check_and_adjust_axis_msg(&a2, n, npy_interned_str.axis2) < 0) { // 检查并调整轴a2
return NULL; // 返回空指针
}
for (i = 0; i < n; ++i) { // 设置初始维度顺序
dims[i] = i;
}
dims[a1] = a2; // 交换轴a1和a2
dims[a2] = a1;
new_axes.ptr = dims; // 设置新轴顺序
new_axes.len = n;
return PyArray_Transpose(ap, &new_axes); // 返回转置后的数组
}
/*
* Transpose a NumPy array.
*/
/*NUMPY_API
* Return Transpose.
*/
NPY_NO_EXPORT PyObject *
PyArray_Transpose(PyArrayObject *ap, PyArray_Dims *permute)
{
npy_intp *axes;
int i, n;
int permutation[NPY_MAXDIMS], reverse_permutation[NPY_MAXDIMS];
PyArrayObject *ret = NULL;
int flags;
if (permute == NULL) { // 如果未指定置换顺序
n = PyArray_NDIM(ap); // 获取数组的维度数
for (i = 0; i < n; i++) {
permutation[i] = n-1-i; // 设置默认逆序置换顺序
}
}
// 更多代码...
}
else {
// 获取置换数组的长度和指针
n = permute->len;
axes = permute->ptr;
// 如果长度与数组维度不匹配,设置错误信息并返回空
if (n != PyArray_NDIM(ap)) {
PyErr_SetString(PyExc_ValueError,
"axes don't match array");
return NULL;
}
// 初始化逆置换数组,全部置为-1
for (i = 0; i < n; i++) {
reverse_permutation[i] = -1;
}
// 对每个轴进行检查和调整
for (i = 0; i < n; i++) {
int axis = axes[i];
// 检查并调整轴的有效性,若无效则返回空
if (check_and_adjust_axis(&axis, PyArray_NDIM(ap)) < 0) {
return NULL;
}
// 如果在逆置换数组中发现重复的轴,设置错误信息并返回空
if (reverse_permutation[axis] != -1) {
PyErr_SetString(PyExc_ValueError,
"repeated axis in transpose");
return NULL;
}
// 更新置换和逆置换数组
reverse_permutation[axis] = i;
permutation[i] = axis;
}
}
// 获取数组的标志位
flags = PyArray_FLAGS(ap);
/*
* 分配内存给维度和步幅(但填充不正确),设置描述符,并将数据指向 PyArray_DATA(ap)。
*/
// 增加描述符的引用计数
Py_INCREF(PyArray_DESCR(ap));
// 使用描述符和基础数据创建新的数组对象
ret = (PyArrayObject *) PyArray_NewFromDescrAndBase(
Py_TYPE(ap), PyArray_DESCR(ap),
n, PyArray_DIMS(ap), NULL, PyArray_DATA(ap),
flags, (PyObject *)ap, (PyObject *)ap);
// 若创建失败,返回空
if (ret == NULL) {
return NULL;
}
/* 调整返回数组的维度和步幅 */
for (i = 0; i < n; i++) {
// 根据置换数组更新维度
PyArray_DIMS(ret)[i] = PyArray_DIMS(ap)[permutation[i]];
// 根据置换数组更新步幅
PyArray_STRIDES(ret)[i] = PyArray_STRIDES(ap)[permutation[i]];
}
// 更新返回数组的标志位
PyArray_UpdateFlags(ret, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS |
NPY_ARRAY_ALIGNED);
// 返回转置后的数组对象
return (PyObject *)ret;
/*
* Return matrix transpose (swap last two dimensions).
*/
NPY_NO_EXPORT PyObject *
PyArray_MatrixTranspose(PyArrayObject *ap)
{
int ndim = PyArray_NDIM(ap); // 获取数组的维度数
if (ndim < 2) { // 如果维度数小于2,抛出数值错误异常
PyErr_SetString(PyExc_ValueError,
"matrix transpose with ndim < 2 is undefined");
return NULL;
}
return PyArray_SwapAxes(ap, ndim - 2, ndim - 1); // 返回交换了最后两个维度的数组对象
}
/*
* Sorts items so stride is descending, because C-order
* is the default in the face of ambiguity.
*/
static int _npy_stride_sort_item_comparator(const void *a, const void *b)
{
npy_intp astride = ((const npy_stride_sort_item *)a)->stride, // 获取结构体元素 a 的步幅
bstride = ((const npy_stride_sort_item *)b)->stride; // 获取结构体元素 b 的步幅
/* Sort the absolute value of the strides */
if (astride < 0) { // 如果步幅为负数,取其绝对值
astride = -astride;
}
if (bstride < 0) { // 如果步幅为负数,取其绝对值
bstride = -bstride;
}
if (astride == bstride) { // 如果步幅相等,则按照排列顺序比较
/*
* Make the qsort stable by next comparing the perm order.
* (Note that two perm entries will never be equal)
*/
npy_intp aperm = ((const npy_stride_sort_item *)a)->perm, // 获取结构体元素 a 的排列顺序
bperm = ((const npy_stride_sort_item *)b)->perm; // 获取结构体元素 b 的排列顺序
return (aperm < bperm) ? -1 : 1; // 返回根据排列顺序比较的结果
}
if (astride > bstride) { // 如果 a 的步幅大于 b 的步幅,返回 -1
return -1;
}
return 1; // 否则返回 1
}
/*NUMPY_API
*
* This function populates the first ndim elements
* of strideperm with sorted descending by their absolute values.
* For example, the stride array (4, -2, 12) becomes
* [(2, 12), (0, 4), (1, -2)].
*/
NPY_NO_EXPORT void
PyArray_CreateSortedStridePerm(int ndim, npy_intp const *strides,
npy_stride_sort_item *out_strideperm)
{
int i;
/* Set up the strideperm values */
for (i = 0; i < ndim; ++i) { // 初始化 strideperm 结构体数组,设置排列顺序和步幅
out_strideperm[i].perm = i; // 设置排列顺序为当前索引 i
out_strideperm[i].stride = strides[i]; // 设置步幅为 strides 数组中对应的值
}
/* Sort them */
qsort(out_strideperm, ndim, sizeof(npy_stride_sort_item),
&_npy_stride_sort_item_comparator); // 使用 qsort 对 strideperm 结构体数组进行排序
}
static inline npy_intp
s_intp_abs(npy_intp x)
{
return (x < 0) ? -x : x; // 返回 x 的绝对值
}
/*
* Creates a sorted stride perm matching the KEEPORDER behavior
* of the NpyIter object. Because this operates based on multiple
* input strides, the 'stride' member of the npy_stride_sort_item
* would be useless and we simply argsort a list of indices instead.
*
* The caller should have already validated that 'ndim' matches for
* every array in the arrays list.
*/
NPY_NO_EXPORT void
PyArray_CreateMultiSortedStridePerm(int narrays, PyArrayObject **arrays,
int ndim, int *out_strideperm)
{
int i0, i1, ipos, ax_j0, ax_j1, iarrays;
/* Initialize the strideperm values to the identity. */
for (i0 = 0; i0 < ndim; ++i0) { // 初始化 strideperm 数组,使其对应于标识
out_strideperm[i0] = i0; // 设置当前索引处的值为当前索引 i0
}
}
/*
* 这段代码实现了一种定制的稳定插入排序,用于NpyIter对象,但以与迭代器相反的顺序排序。
* 迭代器按照最小步长到最大步长的顺序排序(Fortran顺序),而这里按照最大步长到最小步长的顺序排序(C顺序)。
*/
for (i0 = 1; i0 < ndim; ++i0) {
ipos = i0;
ax_j0 = out_strideperm[i0];
for (i1 = i0 - 1; i1 >= 0; --i1) {
int ambig = 1, shouldswap = 0;
ax_j1 = out_strideperm[i1];
for (iarrays = 0; iarrays < narrays; ++iarrays) {
// 检查当前轴上数组的形状是否不是1,如果都不是1,则可能需要交换
if (PyArray_SHAPE(arrays[iarrays])[ax_j0] != 1 &&
PyArray_SHAPE(arrays[iarrays])[ax_j1] != 1) {
// 比较当前两个步长的绝对值大小,决定是否需要交换
if (s_intp_abs(PyArray_STRIDES(arrays[iarrays])[ax_j0]) <=
s_intp_abs(PyArray_STRIDES(arrays[iarrays])[ax_j1])) {
/*
* 即使还不是明确的歧义情况,也设置为需要交换,
* 因为在不同操作数之间的冲突情况下,C顺序优先。
*/
shouldswap = 0;
}
else {
/* 只有在仍然存在歧义时才设置为需要交换 */
if (ambig) {
shouldswap = 1;
}
}
/*
* 已经进行了比较,因此不再是歧义的
*/
ambig = 0;
}
}
/*
* 如果比较是明确的,要么将 'ipos' 移动到 'i1',要么停止寻找插入点
*/
if (!ambig) {
if (shouldswap) {
ipos = i1;
}
else {
break;
}
}
}
/* 将 out_strideperm[i0] 插入到正确的位置 */
if (ipos != i0) {
for (i1 = i0; i1 > ipos; --i1) {
out_strideperm[i1] = out_strideperm[i1-1];
}
out_strideperm[ipos] = ax_j0;
}
}
/*NUMPY_API
* Ravel
* Returns a contiguous array
*/
NPY_NO_EXPORT PyObject *
PyArray_Ravel(PyArrayObject *arr, NPY_ORDER order)
{
// 新维度结构体,用于重塑数组形状
PyArray_Dims newdim = {NULL,1};
// 初始的尺寸值为-1,用于自动计算新形状
npy_intp val[1] = {-1};
newdim.ptr = val;
// 如果指定保持原始顺序
if (order == NPY_KEEPORDER) {
/* This handles some corner cases, such as 0-d arrays as well */
// 处理一些特殊情况,比如0维数组
if (PyArray_IS_C_CONTIGUOUS(arr)) {
order = NPY_CORDER; // 如果是C连续的,使用C顺序
}
else if (PyArray_IS_F_CONTIGUOUS(arr)) {
order = NPY_FORTRANORDER; // 如果是Fortran连续的,使用Fortran顺序
}
}
// 如果指定任意顺序
else if (order == NPY_ANYORDER) {
// 根据数组是否Fortran连续决定顺序
order = PyArray_ISFORTRAN(arr) ? NPY_FORTRANORDER : NPY_CORDER;
}
// 如果顺序是C顺序且数组是C连续的
if (order == NPY_CORDER && PyArray_IS_C_CONTIGUOUS(arr)) {
// 返回一个新的数组,按指定的C顺序重塑
return PyArray_Newshape(arr, &newdim, NPY_CORDER);
}
// 如果顺序是Fortran顺序且数组是Fortran连续的
else if (order == NPY_FORTRANORDER && PyArray_IS_F_CONTIGUOUS(arr)) {
// 返回一个新的数组,按指定的Fortran顺序重塑
return PyArray_Newshape(arr, &newdim, NPY_FORTRANORDER);
}
/* For KEEPORDER, check if we can make a flattened view */
// 对于保持原始顺序,检查是否可以创建扁平视图
else if (order == NPY_KEEPORDER) {
// 创建排序后的步幅列表
npy_stride_sort_item strideperm[NPY_MAXDIMS];
npy_intp stride;
int i, ndim = PyArray_NDIM(arr);
// 创建按步幅排序的排列
PyArray_CreateSortedStridePerm(PyArray_NDIM(arr),
PyArray_STRIDES(arr), strideperm);
// 输出数组必须是连续的,因此第一个步幅是固定的
stride = PyArray_ITEMSIZE(arr);
// 从最后一个维度开始检查
for (i = ndim-1; i >= 0; --i) {
// 大小为1的维度不重要
if (PyArray_DIM(arr, strideperm[i].perm) == 1) {
continue;
}
// 如果步幅不匹配,中断循环
if (strideperm[i].stride != stride) {
break;
}
// 更新步幅
stride *= PyArray_DIM(arr, strideperm[i].perm);
}
// 如果所有步幅匹配连续布局,则返回视图
if (i < 0) {
stride = PyArray_ITEMSIZE(arr);
val[0] = PyArray_SIZE(arr);
// 增加数组的引用计数并返回新数组
Py_INCREF(PyArray_DESCR(arr));
return PyArray_NewFromDescrAndBase(
Py_TYPE(arr), PyArray_DESCR(arr),
1, val, &stride, PyArray_BYTES(arr),
PyArray_FLAGS(arr), (PyObject *)arr, (PyObject *)arr);
}
}
// 否则,返回数组的扁平视图
return PyArray_Flatten(arr, order);
}
}
// 将 ret 转换为 PyObject 指针并返回
return (PyObject *)ret;
/*NUMPY_API
*
* Removes the axes flagged as True from the array,
* modifying it in place. If an axis flagged for removal
* has a shape entry bigger than one, this effectively selects
* index zero for that axis.
*
* WARNING: If an axis flagged for removal has a shape equal to zero,
* the array will point to invalid memory. The caller must
* validate this!
* If an axis flagged for removal has a shape larger than one,
* the aligned flag (and in the future the contiguous flags),
* may need explicit update.
*
* For example, this can be used to remove the reduction axes
* from a reduction result once its computation is complete.
*/
NPY_NO_EXPORT void
PyArray_RemoveAxesInPlace(PyArrayObject *arr, const npy_bool *flags)
{
// 转换数组对象的字段到具体类型
PyArrayObject_fields *fa = (PyArrayObject_fields *)arr;
// 获取数组的维度和步长信息
npy_intp *shape = fa->dimensions, *strides = fa->strides;
int idim, ndim = fa->nd, idim_out = 0;
/* Compress the dimensions and strides */
// 压缩维度和步长信息
for (idim = 0; idim < ndim; ++idim) {
// 如果标志为真,则跳过该维度
if (!flags[idim]) {
// 将非跳过的维度信息复制到输出的维度和步长数组中
shape[idim_out] = shape[idim];
strides[idim_out] = strides[idim];
// 更新输出维度索引
++idim_out;
}
}
/* The final number of dimensions */
// 更新数组对象的维度数
fa->nd = idim_out;
/* NOTE: This is only necessary if a dimension with size != 1 was removed */
// 如果移除了大小不为1的维度,则需要更新连续性标志
PyArray_UpdateFlags(arr, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS);
}
.\numpy\numpy\_core\src\multiarray\shape.h
/*
* 创建一个按照 NpyIter 对象的 KEEPORDER 行为进行排序的步幅排列。
* 因为这是基于多个输入步幅进行操作,np.ndarray 结构体 npy_stride_sort_item 的 'stride' 成员无用,
* 我们简单地对索引列表进行 argsort。
*
* 调用者应该已经验证每个数组在数组列表中的 'ndim' 是否匹配。
*/
NPY_NO_EXPORT void
PyArray_CreateMultiSortedStridePerm(int narrays, PyArrayObject **arrays,
int ndim, int *out_strideperm);
/*
* 类似于 PyArray_Squeeze,但允许调用者选择要挤压的大小为一的维度的子集。
*/
NPY_NO_EXPORT PyObject *
PyArray_SqueezeSelected(PyArrayObject *self, npy_bool *axis_flags);
/*
* 返回矩阵的转置(交换最后两个维度)。
*/
NPY_NO_EXPORT PyObject *
PyArray_MatrixTranspose(PyArrayObject *ap);
/*
* 使用复制模式参数 _copy 进行数组重塑。
*/
NPY_NO_EXPORT PyObject *
_reshape_with_copy_arg(PyArrayObject *array, PyArray_Dims *newdims,
NPY_ORDER order, NPY_COPYMODE copy);
这些注释解释了每个函数的目的和关键参数。每个注释都遵循了代码本身的结构和逻辑顺序。
.\numpy\numpy\_core\src\multiarray\strfuncs.c
/*
* 定义以避免使用已弃用的 NumPy API 版本
*/
/*
* 定义用于多维数组模块的标志
*/
/*
* 清除 PY_SSIZE_T_CLEAN 宏定义,确保在包含 Python.h 之前不会定义 ssize_t
*/
/*
* 包含 Python.h,这是所有 Python C API 的核心头文件
*/
/*
* 包含 NumPy 提供的数组对象头文件
*/
/*
* 包含 NumPy 兼容性模块的头文件
*/
/*
* 包含 NumPy 导入模块的头文件
*/
/*
* 包含多维数组模块的头文件
*/
/*
* 包含字符串处理函数的头文件
*/
/*
* 定义一个静态函数 npy_PyErr_SetStringChained,设置一个字符串类型的异常,并链式传递之前的异常
*/
static void
npy_PyErr_SetStringChained(PyObject *type, const char *message)
{
PyObject *exc, *val, *tb;
/*
* 获取当前的异常信息
*/
PyErr_Fetch(&exc, &val, &tb);
/*
* 设置一个新的异常信息
*/
PyErr_SetString(type, message);
/*
* 将之前捕获的异常信息链式传递
*/
npy_PyErr_ChainExceptionsCause(exc, val, tb);
}
/*
* NUMPY_API
* 将数组打印函数设置为 Python 函数。
* 该函数不会导出给外部模块使用。
*/
NPY_NO_EXPORT void
PyArray_SetStringFunction(PyObject *op, int repr)
{
/*
* 抛出 ValueError 异常,因为 PyArray_SetStringFunction 已被移除
*/
PyErr_SetString(PyExc_ValueError, "PyArray_SetStringFunction was removed");
}
/*
* NUMPY_API
* 返回一个数组对象的字符串表示形式。
* 该函数不会导出给外部模块使用。
*/
NPY_NO_EXPORT PyObject *
array_repr(PyArrayObject *self)
{
/*
* 延迟导入 numpy._core.arrayprint 模块中的 _default_array_repr 函数,
* 避免在模块加载时引起循环导入问题。
*/
npy_cache_import("numpy._core.arrayprint", "_default_array_repr",
&npy_thread_unsafe_state._default_array_repr);
if (npy_thread_unsafe_state._default_array_repr == NULL) {
/*
* 如果无法配置默认的 ndarray.__repr__ 函数,则抛出 RuntimeError 异常
*/
npy_PyErr_SetStringChained(PyExc_RuntimeError,
"Unable to configure default ndarray.__repr__");
return NULL;
}
return PyObject_CallFunctionObjArgs(
npy_thread_unsafe_state._default_array_repr, self, NULL);
}
/*
* NUMPY_API
* 返回一个数组对象的字符串表示形式。
* 该函数不会导出给外部模块使用。
*/
NPY_NO_EXPORT PyObject *
array_str(PyArrayObject *self)
{
/*
* 延迟导入 numpy._core.arrayprint 模块中的 _default_array_str 函数,
* 避免在模块加载时引起循环导入问题。
*/
npy_cache_import("numpy._core.arrayprint", "_default_array_str",
&npy_thread_unsafe_state._default_array_str);
if (npy_thread_unsafe_state._default_array_str == NULL) {
/*
* 如果无法配置默认的 ndarray.__str__ 函数,则抛出 RuntimeError 异常
*/
npy_PyErr_SetStringChained(PyExc_RuntimeError,
"Unable to configure default ndarray.__str__");
return NULL;
}
return PyObject_CallFunctionObjArgs(
npy_thread_unsafe_state._default_array_str, self, NULL);
}
/*
* NUMPY_API
* 格式化数组对象。
* 该函数不会导出给外部模块使用。
*/
NPY_NO_EXPORT PyObject *
array_format(PyArrayObject *self, PyObject *args)
{
PyObject *format;
if (!PyArg_ParseTuple(args, "O:__format__", &format))
return NULL;
/*
* 对于 0 维数组,转发到标量类型
*/
if (PyArray_NDIM(self) == 0) {
PyObject *item = PyArray_ToScalar(PyArray_DATA(self), self);
PyObject *res;
if (item == NULL) {
return NULL;
}
res = PyObject_Format(item, format);
Py_DECREF(item);
return res;
}
/*
* 其他情况下使用内置方法
*/
else {
return PyObject_CallMethod(
(PyObject *)&PyBaseObject_Type, "__format__", "OO",
(PyObject *)self, format
);
}
}
.\numpy\numpy\_core\src\multiarray\strfuncs.h
// 声明一个不导出的函数,用于设置对象的字符串表示函数
NPY_NO_EXPORT void
PyArray_SetStringFunction(PyObject *op, int repr);
// 声明一个不导出的函数,用于返回数组对象的字符串表示形式
NPY_NO_EXPORT PyObject *
array_repr(PyArrayObject *self);
// 声明一个不导出的函数,用于返回数组对象的字符串表示形式
NPY_NO_EXPORT PyObject *
array_str(PyArrayObject *self);
// 声明一个不导出的函数,用于根据指定格式返回数组对象的字符串表示形式
NPY_NO_EXPORT PyObject *
array_format(PyArrayObject *self, PyObject *args);
.\numpy\numpy\_core\src\multiarray\stringdtype\casts.c
/*
定义了一个宏函数 ANY_TO_STRING_RESOLVE_DESCRIPTORS,用于处理类型转换描述符的解析和处理。
此宏函数展开后,会生成一个静态函数 any_to_string_SAFE_resolve_descriptors 和一个静态函数 any_to_string_SAME_KIND_resolve_descriptors。
any_to_string_SAFE_resolve_descriptors 函数:
参数:
- PyObject *NPY_UNUSED(self): 指向 Python 对象的指针,未使用,表示不使用该参数。
- PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]): 指向包含数据类型元信息的数组的指针,未使用,表示不使用该参数。
- PyArray_Descr *given_descrs[2]: 包含两个指向数组数据类型描述符的指针的数组。
- PyArray_Descr *loop_descrs[2]: 用于存储处理后的数据类型描述符的数组。
- npy_intp *NPY_UNUSED(view_offset): 指向整数数组的指针,未使用,表示不使用该参数。
函数逻辑:
- 检查给定的第二个描述符 given_descrs[1] 是否为 NULL。
- 如果 given_descrs[1] 为 NULL,则创建一个新的字符串数据类型描述符 new,并将其存储在 loop_descrs[1] 中。
- 如果 given_descrs[1] 不为 NULL,则增加其引用计数,并将其存储在 loop_descrs[1] 中。
- 增加给定的第一个描述符 given_descrs[0] 的引用计数,并将其存储在 loop_descrs[0] 中。
- 返回 NPY_SAFE_CASTING。
any_to_string_SAME_KIND_resolve_descriptors 函数:
参数和逻辑与 any_to_string_SAFE_resolve_descriptors 函数类似,区别在于最后返回的是 NPY_SAME_KIND_CASTING。
static NPY_CASTING
string_to_string_resolve_descriptors(PyObject *NPY_UNUSED(self),
PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]),
PyArray_Descr *given_descrs[2],
PyArray_Descr *loop_descrs[2],
npy_intp *view_offset)
{
*/
/*
上述代码段中的注释主要是解释了宏定义 ANY_TO_STRING_RESOLVE_DESCRIPTORS 的作用和展开后的两个静态函数 any_to_string_SAFE_resolve_descriptors 和 any_to_string_SAME_KIND_resolve_descriptors 的参数及逻辑。
*/
if (given_descrs[1] == NULL) {
loop_descrs[1] = stringdtype_finalize_descr(given_descrs[0]);
}
else {
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
}
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
PyArray_StringDTypeObject *descr0 = (PyArray_StringDTypeObject *)loop_descrs[0];
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)loop_descrs[1];
if ((descr0->na_object != NULL) && (descr1->na_object == NULL)) {
// 从带有 NA 的 dtype 转换到没有 NA 的 dtype 是不安全的,因为会丢失信息
// 从没有 NA 的 dtype 转换到带有 NA 的 dtype 是安全的,因为源数据没有 NA 可能丢失
return NPY_UNSAFE_CASTING;
}
if (descr0->allocator == descr1->allocator) {
// 如果描述符共享相同的分配器,则视图偏移量设置为 0
*view_offset = 0;
};
return NPY_NO_CASTING;
}
static int
string_to_string(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取输入和输出的字符串描述符
PyArray_StringDTypeObject *idescr = (PyArray_StringDTypeObject *)context->descriptors[0];
PyArray_StringDTypeObject *odescr = (PyArray_StringDTypeObject *)context->descriptors[1];
// 检查输入和输出是否包含空值
int in_has_null = idescr->na_object != NULL;
int out_has_null = odescr->na_object != NULL;
// 获取输入的空值名称
const npy_static_string *in_na_name = &idescr->na_name;
// 获取数据的维度
npy_intp N = dimensions[0];
// 获取输入和输出数据指针
char *in = data[0];
char *out = data[1];
// 获取输入和输出的步长
npy_intp in_stride = strides[0];
npy_intp out_stride = strides[1];
// 分配输入和输出的字符串分配器
npy_string_allocator *allocators[2] = {NULL, NULL};
NpyString_acquire_allocators(2, context->descriptors, allocators);
npy_string_allocator *iallocator = allocators[0];
npy_string_allocator *oallocator = allocators[1];
while (N--) {
// 读取输入字符串
const npy_packed_static_string *s = (npy_packed_static_string *)in;
// 获取输出字符串
npy_packed_static_string *os = (npy_packed_static_string *)out;
// 如果输入和输出不共享内存
if (!NpyString_share_memory(s, iallocator, os, oallocator)) {
// 如果输入包含空值但输出不包含,并且输入字符串是空值
if (in_has_null && !out_has_null && NpyString_isnull(s)) {
// 执行不安全的转换,将空值包装为输出字符串
if (NpyString_pack(oallocator, os, in_na_name->buf,
in_na_name->size) < 0) {
// 内存错误处理
npy_gil_error(PyExc_MemoryError,
"Failed to pack string in string to string "
"cast.");
goto fail;
}
}
// 否则,执行自由和复制操作
else if (free_and_copy(iallocator, oallocator, s, os,
"string to string cast") < 0) {
goto fail;
}
}
// 更新输入和输出指针位置
in += in_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocators(2, allocators);
return 0;
fail:
// 失败时释放字符串分配器并返回错误码
NpyString_release_allocators(2, allocators);
return -1;
}
static PyType_Slot s2s_slots[] = {
// 解析描述符方法槽
{NPY_METH_resolve_descriptors, &string_to_string_resolve_descriptors},
// 字符串到字符串的循环处理方法槽
{NPY_METH_strided_loop, &string_to_string},
// 不对齐的字符串到字符串循环处理方法槽
{NPY_METH_unaligned_strided_loop, &string_to_string},
// 终止槽
{0, NULL}};
static char *s2s_name = "cast_StringDType_to_StringDType";
// unicode to string
static int
unicode_to_string(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取描述符数组
PyArray_Descr *const *descrs = context->descriptors;
// 获取输出描述符
PyArray_StringDTypeObject *sdescr = (PyArray_StringDTypeObject *)descrs[1];
// 获取输出字符串的分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(sdescr);
// 计算最大输入大小
long max_in_size = (descrs[0]->elsize) / sizeof(Py_UCS4);
// 获取数据的维度
npy_intp N = dimensions[0];
// 获取输入和输出数据指针
Py_UCS4 *in = (Py_UCS4 *)data[0];
char *out = data[1];
npy_intp in_stride = strides[0] / sizeof(Py_UCS4);
npy_intp out_stride = strides[1];
while (N--) {
size_t out_num_bytes = 0;
size_t num_codepoints = 0;
if (utf8_size(in, max_in_size, &num_codepoints, &out_num_bytes) ==
-1) {
npy_gil_error(PyExc_TypeError, "Invalid unicode code point found");
goto fail;
}
npy_static_string out_ss = {0, NULL};
if (load_new_string((npy_packed_static_string *)out,
&out_ss, out_num_bytes, allocator,
"unicode to string cast") == -1) {
goto fail;
}
// 忽略常量以填充缓冲区
char *out_buf = (char *)out_ss.buf;
for (size_t i = 0; i < num_codepoints; i++) {
Py_UCS4 code = in[i];
char utf8_c[4] = {0};
size_t num_bytes = ucs4_code_to_utf8_char(code, utf8_c);
strncpy(out_buf, utf8_c, num_bytes);
out_buf += num_bytes;
}
out_buf -= out_num_bytes;
in += in_stride;
out += out_stride;
}
NpyString_release_allocator(allocator);
return 0;
// 释放 NpyString 分配的内存空间的分配器
NpyString_release_allocator(allocator);
// 返回 -1,表示函数执行失败
return -1;
}
// 定义 PyType_Slot 数组,包含解析描述符和相应的函数指针
static PyType_Slot u2s_slots[] = {{NPY_METH_resolve_descriptors,
&any_to_string_SAME_KIND_resolve_descriptors},
{NPY_METH_strided_loop, &unicode_to_string},
{0, NULL}};
// 定义字符串 u2s_name,用于类型转换从 Unicode 到 StringDType
static char *u2s_name = "cast_Unicode_to_StringDType";
// 将字符串转换为固定宽度数据类型时的解析描述符处理函数
static NPY_CASTING
string_to_fixed_width_resolve_descriptors(
PyObject *NPY_UNUSED(self),
PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]),
PyArray_Descr *given_descrs[2],
PyArray_Descr *loop_descrs[2],
npy_intp *NPY_UNUSED(view_offset))
{
if (given_descrs[1] == NULL) {
// 如果给定描述符为 NULL,则设置类型错误并返回 -1
// 表示当前不支持从 StringDType 到固定宽度数据类型的转换
PyErr_SetString(
PyExc_TypeError,
"Casting from StringDType to a fixed-width dtype with an "
"unspecified size is not currently supported, specify "
"an explicit size for the output dtype instead.");
return (NPY_CASTING)-1;
}
else {
// 增加给定描述符的引用计数,并设置循环描述符
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
}
// 增加给定描述符的引用计数,并设置循环描述符
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
// 返回相同类型的类型转换标志
return NPY_SAME_KIND_CASTING;
}
// 加载可空字符串的函数,从静态字符串中加载到动态分配的字符串中
static int
load_nullable_string(const npy_packed_static_string *ps,
npy_static_string *s,
int has_null,
int has_string_na,
const npy_static_string *default_string,
const npy_static_string *na_name,
npy_string_allocator *allocator,
char *context)
{
// 使用 NpyString_load 函数加载字符串,如果加载失败则报错并返回 -1
int is_null = NpyString_load(allocator, ps, s);
if (is_null == -1) {
npy_gil_error(PyExc_MemoryError,
"Failed to load string in %s", context);
return -1;
}
else if (is_null) {
if (has_null && !has_string_na) {
// 如果字符串为 NULL,且支持 NULL,但没有设置字符串 NA,则使用 NA 名称
// 否则使用默认字符串
*s = *na_name;
}
else {
*s = *default_string;
}
}
return 0;
}
// 字符串到 Unicode 的转换函数,处理 PyArrayMethod_Context 上下文和数据
static int
string_to_unicode(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取字符串类型的描述符
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 检查是否有 NULL 值对象
int has_null = descr->na_object != NULL;
// 检查是否有字符串 NA
int has_string_na = descr->has_string_na;
// 默认字符串
const npy_static_string *default_string = &descr->default_string;
// NA 名称
const npy_static_string *na_name = &descr->na_name;
// 获取数据的维度大小
npy_intp N = dimensions[0];
// 输入数据的指针
char *in = data[0];
// 输出数据的指针
Py_UCS4 *out = (Py_UCS4 *)data[1];
// 输入数据的步长
npy_intp in_stride = strides[0];
// 输出数据的步长,转换为 Py_UCS4 的大小
npy_intp out_stride = strides[1] / sizeof(Py_UCS4);
// 计算每个输出的最大 UCS4 字符数
size_t max_out_size = (context->descriptors[1]->elsize) / sizeof(Py_UCS4);
// 循环处理输入数据,N 表示迭代次数
while (N--) {
// 从输入中读取一个静态字符串结构体 ps
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
// 初始化一个静态字符串结构体 s
npy_static_string s = {0, NULL};
// 加载可空字符串,转换为 Unicode 字符串 s
if (load_nullable_string(ps, &s, has_null, has_string_na,
default_string, na_name, allocator,
"in string to unicode cast") == -1) {
// 如果加载失败,跳转到失败处理标签
goto fail;
}
// 转换为无符号字符指针
unsigned char *this_string = (unsigned char *)(s.buf);
// 字符串字节数
size_t n_bytes = s.size;
// 总字节数
size_t tot_n_bytes = 0;
// 如果字符串长度为0,填充输出数组为0
if (n_bytes == 0) {
for (int i=0; i < max_out_size; i++) {
out[i] = (Py_UCS4)0;
}
}
else {
int i = 0;
// 将 UTF-8 字符转换为 UCS4 编码,填充输出数组直到达到最大输出长度或者处理完所有字节
for (; i < max_out_size && tot_n_bytes < n_bytes; i++) {
int num_bytes = utf8_char_to_ucs4_code(this_string, &out[i]);
// 移动到下一个字符
this_string += num_bytes;
tot_n_bytes += num_bytes;
}
// 如果未填充满最大输出长度,剩余部分填充为0
for(; i < max_out_size; i++) {
out[i] = (Py_UCS4)0;
}
}
// 更新输入和输出指针,以及释放分配器资源
in += in_stride;
out += out_stride;
}
// 释放分配器资源
NpyString_release_allocator(allocator);
// 返回成功状态
return 0;
// 释放分配的内存,并返回失败代码
fail:
NpyString_release_allocator(allocator);
return -1;
}
// 定义一个静态的 PyType_Slot 数组,包含解析描述符和字符串到Unicode的函数指针
static PyType_Slot s2u_slots[] = {
{NPY_METH_resolve_descriptors, &string_to_fixed_width_resolve_descriptors},
{NPY_METH_strided_loop, &string_to_unicode},
{0, NULL}};
// 定义一个静态的字符串,用于表示函数名称
static char *s2u_name = "cast_StringDType_to_Unicode";
// string to bool
// 解析描述符的函数,将字符串转换为布尔值
static NPY_CASTING
string_to_bool_resolve_descriptors(PyObject *NPY_UNUSED(self),
PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]),
PyArray_Descr *given_descrs[2],
PyArray_Descr *loop_descrs[2],
npy_intp *NPY_UNUSED(view_offset))
{
// 如果给定的第二个描述符为空,则使用 NPY_BOOL 类型创建一个新的描述符
if (given_descrs[1] == NULL) {
loop_descrs[1] = PyArray_DescrNewFromType(NPY_BOOL);
}
else {
// 否则增加给定描述符的引用计数并使用它
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
}
// 增加给定描述符的引用计数并使用它
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
// 返回不安全的强制转换类型
return NPY_UNSAFE_CASTING;
}
// 将字符串转换为布尔值的函数
static int
string_to_bool(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取描述符对象
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 检查是否有 NULL 对象
int has_null = descr->na_object != NULL;
// 检查是否有字符串 NA
int has_string_na = descr->has_string_na;
// 检查是否有 NaN NA
int has_nan_na = descr->has_nan_na;
// 获取默认字符串
const npy_static_string *default_string = &descr->default_string;
// 获取数据维度
npy_intp N = dimensions[0];
// 输入数据指针
char *in = data[0];
// 输出数据指针
char *out = data[1];
// 输入数据步长
npy_intp in_stride = strides[0];
// 输出数据步长
npy_intp out_stride = strides[1];
// 遍历数据进行转换
while (N--) {
// 获取紧凑静态字符串对象
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
// 初始化静态字符串对象
npy_static_string s = {0, NULL};
// 加载字符串到静态字符串对象中
int is_null = NpyString_load(allocator, ps, &s);
// 如果加载失败则报错并跳转到失败标签
if (is_null == -1) {
npy_gil_error(PyExc_MemoryError, "Failed to load string in string to bool cast");
goto fail;
}
// 如果字符串为空
else if (is_null) {
// 如果存在 NULL 对象且不存在字符串 NA
if (has_null && !has_string_na) {
// 如果存在 NaN NA,则将其视为真值,按照 Python 的规则
if (has_nan_na) {
*out = NPY_TRUE;
}
// 否则将其视为假值
else {
*out = NPY_FALSE;
}
}
// 如果不存在 NULL 对象,则将默认字符串的大小作为条件进行判断
else {
*out = (npy_bool)(default_string->size == 0);
}
}
// 如果字符串长度为零,则将输出设置为假值
else if (s.size == 0) {
*out = NPY_FALSE;
}
// 否则将输出设置为真值
else {
*out = NPY_TRUE;
}
// 更新输入和输出指针
in += in_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocator(allocator);
return 0;
// 失败时释放字符串分配器并返回失败代码
fail:
NpyString_release_allocator(allocator);
return -1;
}
// 定义 PyType_Slot 结构体数组 s2b_slots,用于描述字符串到布尔类型转换的方法
static PyType_Slot s2b_slots[] = {
// 解析描述符方法,使用 string_to_bool_resolve_descriptors 函数
{NPY_METH_resolve_descriptors, &string_to_bool_resolve_descriptors},
// 使用 string_to_bool 函数进行跨步循环方法
{NPY_METH_strided_loop, &string_to_bool},
// 数组结束标记
{0, NULL}
};
// 字符串 "cast_StringDType_to_Bool",用于标识字符串到布尔类型转换
static char *s2b_name = "cast_StringDType_to_Bool";
// 布尔到字符串的转换
// 定义 bool_to_string 函数,将布尔值转换为字符串
static int
bool_to_string(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取数组维度大小
npy_intp N = dimensions[0];
// 输入数据指针
char *in = data[0];
// 输出数据指针
char *out = data[1];
// 输入和输出的步长
npy_intp in_stride = strides[0];
npy_intp out_stride = strides[1];
// 获取描述符对象
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[1];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 循环处理每个元素
while (N--) {
// 输出数据转换为静态字符串指针
npy_packed_static_string *out_pss = (npy_packed_static_string *)out;
// 返回值字符串指针初始化为空
char *ret_val = NULL;
// 返回值字符串大小初始化为0
size_t size = 0;
// 根据布尔值设置返回值和大小
if ((npy_bool)(*in) == NPY_TRUE) {
ret_val = "True";
size = 4;
}
else if ((npy_bool)(*in) == NPY_FALSE) {
ret_val = "False";
size = 5;
}
else {
// 如果布尔值无效,则引发运行时错误
npy_gil_error(PyExc_RuntimeError,
"invalid value encountered in bool to string cast");
// 转到错误处理标签
goto fail;
}
// 尝试将返回值打包到输出静态字符串指针中
if (NpyString_pack(allocator, out_pss, ret_val, size) < 0) {
// 如果打包失败,则引发内存错误
npy_gil_error(PyExc_MemoryError,
"Failed to pack string in bool to string cast");
// 转到错误处理标签
goto fail;
}
// 更新输入和输出指针位置
in += in_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocator(allocator);
// 返回成功状态
return 0;
fail:
// 失败时释放字符串分配器
NpyString_release_allocator(allocator);
// 返回失败状态
return -1;
}
// 定义 PyType_Slot 结构体数组 b2s_slots,用于描述布尔到字符串类型转换的方法
static PyType_Slot b2s_slots[] = {
// 解析描述符方法,使用 any_to_string_SAFE_resolve_descriptors 函数
{NPY_METH_resolve_descriptors, &any_to_string_SAFE_resolve_descriptors},
// 使用 bool_to_string 函数进行跨步循环方法
{NPY_METH_strided_loop, &bool_to_string},
// 数组结束标记
{0, NULL}
};
// 字符串 "cast_Bool_to_StringDType",用于标识布尔到字符串类型转换
static char *b2s_name = "cast_Bool_to_StringDType";
// 字符串和 (u)int 数据类型之间的转换
// 定义 load_non_nullable_string 函数,加载非空字符串并进行类型转换
static int
load_non_nullable_string(char *in, int has_null, const npy_static_string *default_string,
npy_static_string *string_to_load, npy_string_allocator *allocator,
int has_gil)
{
// 输入静态字符串指针转换为打包的静态字符串指针
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
// 加载静态字符串并返回是否为空
int isnull = NpyString_load(allocator, ps, string_to_load);
// 如果加载失败
if (isnull == -1) {
// 错误消息
char *msg = "Failed to load string for conversion to a non-nullable type";
// 如果有全局解释器锁
if (has_gil) {
// 设置内存错误异常消息
PyErr_SetString(PyExc_MemoryError, msg);
}
else {
// 否则,引发内存错误
npy_gil_error(PyExc_MemoryError, msg);
}
// 返回加载失败状态
return -1;
}
// 加载成功,返回成功状态
return 0;
}
else if (isnull) {
// 如果数组中有空值
if (has_null) {
// 错误消息
char *msg = "Arrays with missing data cannot be converted to a non-nullable type";
// 如果已经获取了全局解释器锁
if (has_gil)
{
// 设置 Python 异常对象为 ValueError,并传入错误消息
PyErr_SetString(PyExc_ValueError, msg);
}
else {
// 否则,调用 numpy 的 GIL 错误处理函数,设置 Python 异常为 ValueError,并传入错误消息
npy_gil_error(PyExc_ValueError, msg);
}
// 返回 -1,表示操作失败
return -1;
}
// 如果数组中没有空值,则将默认字符串复制到要加载的字符串中
*string_to_load = *default_string;
}
// 操作成功,返回 0
return 0;
// 这是一个静态函数,将非空字符串转换为 Python 字符串对象。
// 如果转换过程中遇到问题,返回 NULL。
static PyObject *
non_nullable_string_to_pystring(char *in, int has_null, const npy_static_string *default_string,
npy_string_allocator *allocator)
{
// 创建一个静态字符串结构体 s,初始化为空
npy_static_string s = {0, NULL};
// 调用 load_non_nullable_string 函数,将输入的非空字符串转换为 s.buf 中的字符串
if (load_non_nullable_string(in, has_null, default_string, &s, allocator, 1) == -1) {
return NULL;
}
// 使用 s.buf 和 s.size 创建一个新的 Python Unicode 字符串对象
PyObject *val_obj = PyUnicode_FromStringAndSize(s.buf, s.size);
// 如果创建过程中出错,返回 NULL
if (val_obj == NULL) {
return NULL;
}
// 返回创建的 Python 字符串对象
return val_obj;
}
// 这是一个静态函数,将字符串转换为 Python 长整型对象。
// 如果转换过程中遇到问题,返回 NULL。
static PyObject *
string_to_pylong(char *in, int has_null,
const npy_static_string *default_string,
npy_string_allocator *allocator)
{
// 调用 non_nullable_string_to_pystring 函数,将输入的字符串转换为 Python 字符串对象
PyObject *val_obj = non_nullable_string_to_pystring(
in, has_null, default_string, allocator);
// 如果转换过程中出错,返回 NULL
if (val_obj == NULL) {
return NULL;
}
// 将 Python 字符串对象解释为十进制整数
PyObject *pylong_value = PyLong_FromUnicodeObject(val_obj, 10);
// 减少对 val_obj 的引用计数
Py_DECREF(val_obj);
// 返回创建的 Python 长整型对象
return pylong_value;
}
// 这是一个静态函数,将字符串转换为无符号长长整型数。
// 如果转换过程中出错,返回 -1;否则返回 0。
static npy_longlong
stringbuf_to_uint(char *in, npy_ulonglong *value, int has_null,
const npy_static_string *default_string,
npy_string_allocator *allocator)
{
// 调用 string_to_pylong 函数,将输入的字符串转换为 Python 长整型对象
PyObject *pylong_value =
string_to_pylong(in, has_null, default_string, allocator);
// 如果转换过程中出错,返回 -1
if (pylong_value == NULL) {
return -1;
}
// 将 Python 长整型对象转换为无符号长长整型数
*value = PyLong_AsUnsignedLongLong(pylong_value);
// 如果转换过程中出错,释放 Python 长整型对象并返回 -1
if (*value == (unsigned long long)-1 && PyErr_Occurred()) {
Py_DECREF(pylong_value);
return -1;
}
// 释放 Python 长整型对象并返回 0
Py_DECREF(pylong_value);
return 0;
}
// 这是一个静态函数,将字符串转换为有符号长长整型数。
// 如果转换过程中出错,返回 -1;否则返回 0。
static npy_longlong
stringbuf_to_int(char *in, npy_longlong *value, int has_null,
const npy_static_string *default_string,
npy_string_allocator *allocator)
{
// 调用 string_to_pylong 函数,将输入的字符串转换为 Python 长整型对象
PyObject *pylong_value =
string_to_pylong(in, has_null, default_string, allocator);
// 如果转换过程中出错,返回 -1
if (pylong_value == NULL) {
return -1;
}
// 将 Python 长整型对象转换为有符号长长整型数
*value = PyLong_AsLongLong(pylong_value);
// 如果转换过程中出错,释放 Python 长整型对象并返回 -1
if (*value == -1 && PyErr_Occurred()) {
Py_DECREF(pylong_value);
return -1;
}
// 释放 Python 长整型对象并返回 0
Py_DECREF(pylong_value);
return 0;
}
// 这是一个静态函数,将 Python 对象转换为字符串。
// 如果转换过程中出错,返回 -1;否则返回 0。
static int
pyobj_to_string(PyObject *obj, char *out, npy_string_allocator *allocator)
{
// 如果输入的 Python 对象为空,返回 -1
if (obj == NULL) {
return -1;
}
// 将 Python 对象转换为字符串对象
PyObject *pystr_val = PyObject_Str(obj);
// 减少对输入 Python 对象的引用计数
Py_DECREF(obj);
// 如果转换过程中出错,返回 -1
if (pystr_val == NULL) {
return -1;
}
// 将 Python 字符串对象转换为 UTF-8 编码的 C 字符串
Py_ssize_t length;
const char *cstr_val = PyUnicode_AsUTF8AndSize(pystr_val, &length);
// 如果转换过程中出错,释放 Python 字符串对象并返回 -1
if (cstr_val == NULL) {
Py_DECREF(pystr_val);
return -1;
}
// 将 UTF-8 编码的 C 字符串打包到输出的字符串结构体中
npy_packed_static_string *out_ss = (npy_packed_static_string *)out;
if (NpyString_pack(allocator, out_ss, cstr_val, length) < 0) {
// 如果打包过程中出错,报告内存错误,并释放 Python 字符串对象
npy_gil_error(PyExc_MemoryError,
"Failed to pack string while converting from python "
"string");
Py_DECREF(pystr_val);
return -1;
}
// 释放 Python 字符串对象并返回 0
Py_DECREF(pystr_val);
return 0;
}
// 递减 Python 对象的引用计数,当引用计数为零时自动释放对象
Py_DECREF(pystr_val);
// 返回整数 0,表示函数执行成功
return 0;
}
// 结束静态函数定义
static int
int_to_stringbuf(long long in, char *out, npy_string_allocator *allocator)
{
// 将 long long 类型的输入转换为 Python 的长整型对象
PyObject *pylong_val = PyLong_FromLongLong(in);
// 调用通用函数将 Python 对象转换为字符串,并使用分配器进行内存管理
return pyobj_to_string(pylong_val, out, allocator);
}
static int
uint_to_stringbuf(unsigned long long in, char *out,
npy_string_allocator *allocator)
{
// 将 unsigned long long 类型的输入转换为 Python 的无符号长整型对象
PyObject *pylong_val = PyLong_FromUnsignedLongLong(in);
// 调用通用函数将 Python 对象转换为字符串,并使用分配器进行内存管理
return pyobj_to_string(pylong_val, out, allocator);
}
// 获取 string 到 typename 和 typename 到 string 的数据类型元信息
PyArray_DTypeMeta **s2
&PyArray_StringDType, \
&PyArray_
\
// 获取 string 到 typename 的类型转换规范
PyArrayMethod_Spec *StringTo
get_cast_spec( \
s2
NPY_METH_REQUIRES_PYAPI, s2
s2
\
// 获取 typename 到 string 的类型转换规范
PyArray_DTypeMeta **shortname
&PyArray_
&PyArray_StringDType); \
\
// 获取 typename 到 string 的类型转换规范
PyArrayMethod_Spec *typename
shortname
NPY_METH_REQUIRES_PYAPI, shortname
shortname
// 定义宏 DTYPES_AND_CAST_SPEC,用于生成不同类型之间的转换规范
STRING_INT_CASTS(int8, int, i8, NPY_INT8, lli, npy_longlong, long long)
// 宏 STRING_INT_CASTS 的实例化:定义 int8 类型转换规范
STRING_INT_CASTS(int16, int, i16, NPY_INT16, lli, npy_longlong, long long)
// 宏 STRING_INT_CASTS 的实例化:定义 int16 类型转换规范
STRING_INT_CASTS(int32, int, i32, NPY_INT32, lli, npy_longlong, long long)
// 宏 STRING_INT_CASTS 的实例化:定义 int32 类型转换规范
STRING_INT_CASTS(int64, int, i64, NPY_INT64, lli, npy_longlong, long long)
// 宏 STRING_INT_CASTS 的实例化:定义 int64 类型转换规范
STRING_INT_CASTS(uint8, uint, u8, NPY_UINT8, llu, npy_ulonglong,
unsigned long long)
// 宏 STRING_INT_CASTS 的实例化:定义 uint8 类型转换规范
STRING_INT_CASTS(uint16, uint, u16, NPY_UINT16, llu, npy_ulonglong,
unsigned long long)
// 宏 STRING_INT_CASTS 的实例化:定义 uint16 类型转换规范
STRING_INT_CASTS(uint32, uint, u32, NPY_UINT32, llu, npy_ulonglong,
unsigned long long)
// 宏 STRING_INT_CASTS 的实例化:定义 uint32 类型转换规范
STRING_INT_CASTS(uint64, uint, u64, NPY_UINT64, llu, npy_ulonglong,
unsigned long long)
// 宏 STRING_INT_CASTS 的实例化:定义 uint64 类型转换规范
// 如果 byte 的大小等于 short 的大小
// 宏 STRING_INT_CASTS 的实例化:定义 byte 类型转换规范
STRING_INT_CASTS(byte, int, byte, NPY_BYTE, lli, npy_longlong, long long)
// 宏 STRING_INT_CASTS 的实例化:定义 ubyte 类型转换规范
STRING_INT_CASTS(ubyte, uint, ubyte, NPY_UBYTE, llu, npy_ulonglong,
unsigned long long)
// 如果 short 的大小等于 int 的大小
// 定义宏,用于生成字符串到整数类型的转换函数和相关信息的结构体定义
lli, npy_longlong, long long) \
// 定义函数 string_to_
static int string_to_
char *const data[], \
npy_intp const dimensions[], \
npy_intp const strides[], \
NpyAuxData *NPY_UNUSED(auxdata)) \
// 定义失败处理标签 fail,用于在出错时释放分配的字符串内存并返回错误
fail: \
NpyString_release_allocator(allocator); \
return -1; \
} \
\
// 定义 PyType_Slot 结构体数组 s2
static PyType_Slot s2
// 解析描述符方法,关联到函数 string_to_
{NPY_METH_resolve_descriptors, \
&string_to_
// 循环方法,关联到函数 string_to_
{NPY_METH_strided_loop, &string_to_
// 结束标记
{0, NULL}}; \
\
// 定义字符串常量 s2
static char *s2
// 定义宏,用于生成字符串到浮点数类型的转换函数和相关信息的结构体定义
double_to_float) \
// 定义函数 string_to_
static int string_to_
char *const data[], \
npy_intp const dimensions[], \
npy_intp const strides[], \
NpyAuxData *NPY_UNUSED(auxdata)) \
// 定义失败处理标签 fail,用于在出错时释放分配的字符串内存并返回错误
fail: \
NpyString_release_allocator(allocator); \
return -1; \
} \
\
// 定义 PyType_Slot 结构体数组 s2
static PyType_Slot s2
// 解析描述符方法,关联到函数 string_to_
{NPY_METH_resolve_descriptors, \
&string_to_
// 循环方法,关联到函数 string_to_
{NPY_METH_strided_loop, &string_to_
// 结束标记
{0, NULL}}; \
\
// 定义字符串常量 s2
static char *s2
// 空的宏定义,用于未实现的宏参数
// 定义一个静态函数 string_to_
// 该函数用于解析描述符并进行类型转换
static NPY_CASTING string_to_
// 不使用 self 参数
PyObject *NPY_UNUSED(self), \
// 不使用 dtypes 数组
PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), \
// 输入的描述符数组和循环使用的描述符数组
PyArray_Descr *given_descrs[2], PyArray_Descr *loop_descrs[2], \
// 视图偏移量,不使用此参数
npy_intp *NPY_UNUSED(view_offset)) \
{ \
// 如果第二个给定的描述符为空
if (given_descrs[1] == NULL) { \
// 根据给定的 numpy 类型创建新的描述符并存储在循环描述符数组中
loop_descrs[1] = PyArray_DescrNewFromType(NPY_
} \
else { \
// 增加给定描述符的引用计数并复制到循环描述符数组中
Py_INCREF(given_descrs[1]); \
loop_descrs[1] = given_descrs[1]; \
} \
\
// 增加给定描述符的引用计数并复制到循环描述符数组中
Py_INCREF(given_descrs[0]); \
loop_descrs[0] = given_descrs[0]; \
\
// 返回非安全转换标志
return NPY_UNSAFE_CASTING; \
}
// 定义宏 FLOAT_TO_STRING_CAST,用于生成将特定类型的数组转换为字符串的函数
// 定义函数 typename
static int typename
PyArrayMethod_Context *context, char *const data[], \
npy_intp const dimensions[], npy_intp const strides[], \
NpyAuxData *NPY_UNUSED(auxdata)) \
{ \
// 获取数组的第一个维度大小
npy_intp N = dimensions[0]; \
// 将输入数据解释为指定类型的数组
npy_
// 输出字符串的起始位置
char *out = data[1]; \
// 获取浮点数的描述器
PyArray_Descr *float_descr = context->descriptors[0]; \
\
// 计算输入数组的步长(每个元素占据的字节数除以数据类型的大小)
npy_intp in_stride = strides[0] / sizeof(npy_
// 输出数组的步长
npy_intp out_stride = strides[1]; \
\
// 获取输出字符串的描述器对象
PyArray_StringDTypeObject *descr = \
(PyArray_StringDTypeObject *)context->descriptors[1]; \
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr); \
\
// 循环处理每个元素
while (N--) { \
// 将输入数组中的当前值转换为 Python 对象
PyObject *scalar_val = PyArray_Scalar(in, float_descr, NULL); \
// 将 Python 对象转换为字符串并存储到输出位置,使用指定的分配器
if (pyobj_to_string(scalar_val, out, allocator) == -1) { \
// 转换失败时跳转到失败处理标签
goto fail; \
} \
\
// 更新输入和输出指针到下一个元素
in += in_stride; \
out += out_stride; \
} \
\
// 释放字符串分配器
NpyString_release_allocator(allocator); \
// 返回成功状态
return 0; \
fail: \
// 失败时释放字符串分配器并返回失败状态
NpyString_release_allocator(allocator); \
return -1; \
}
// 定义 PyType_Slot 结构体数组,用于实现类型转换
static PyType_Slot shortname
// 设置解析描述符方法,指向 any_to_string_SAFE_resolve_descriptors 函数
{NPY_METH_resolve_descriptors, \
&any_to_string_SAFE_resolve_descriptors}, \
// 设置跨步循环方法,指向 typename
{NPY_METH_strided_loop, &typename
// 终止标记,空槽和空指针
{0, NULL}}; \
\
// 定义 shortname
static char *shortname
STRING_TO_FLOAT_RESOLVE_DESCRIPTORS(float64, DOUBLE)
static int
string_to_float64(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
int has_null = descr->na_object != NULL;
const npy_static_string *default_string = &descr->default_string;
npy_intp N = dimensions[0];
char *in = data[0];
npy_float64 *out = (npy_float64 *)data[1];
npy_intp in_stride = strides[0];
npy_intp out_stride = strides[1] / sizeof(npy_float64);
while (N--) {
PyObject *pyfloat_value =
string_to_pyfloat(in, has_null, default_string, allocator);
if (pyfloat_value == NULL) {
goto fail;
}
*out = (npy_float64)PyFloat_AS_DOUBLE(pyfloat_value);
Py_DECREF(pyfloat_value);
in += in_stride;
out += out_stride;
}
NpyString_release_allocator(allocator);
return 0;
fail:
NpyString_release_allocator(allocator);
return -1;
}
static PyType_Slot s2f64_slots[] = {
{NPY_METH_resolve_descriptors, &string_to_float64_resolve_descriptors},
{NPY_METH_strided_loop, &string_to_float64},
{0, NULL}};
static char *s2f64_name = "cast_StringDType_to_float64";
FLOAT_TO_STRING_CAST(float64, f64, double)
STRING_TO_FLOAT_RESOLVE_DESCRIPTORS(float32, FLOAT)
STRING_TO_FLOAT_CAST(float32, f32, npy_isinf, npy_float32)
FLOAT_TO_STRING_CAST(float32, f32, double)
STRING_TO_FLOAT_RESOLVE_DESCRIPTORS(float16, HALF)
STRING_TO_FLOAT_CAST(float16, f16, npy_half_isinf, npy_double_to_half)
FLOAT_TO_STRING_CAST(float16, f16, npy_half_to_double)
STRING_TO_FLOAT_RESOLVE_DESCRIPTORS(longdouble, LONGDOUBLE);
static int
string_to_longdouble(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
int has_null = descr->na_object != NULL;
const npy_static_string *default_string = &descr->default_string;
npy_intp N = dimensions[0];
char *in = data[0];
npy_longdouble *out = (npy_longdouble *)data[1];
npy_intp in_stride = strides[0];
npy_intp out_stride = strides[1] / sizeof(npy_longdouble);
// 循环,逐个处理直到 N 减少为零
while (N--) {
// 定义静态字符串结构体并初始化为空
npy_static_string s = {0, NULL};
// 尝试从输入流中加载非空字符串,如果失败则跳转到失败标签
if (load_non_nullable_string(in, has_null, default_string, &s, allocator, 0) == -1) {
goto fail;
}
// 分配临时的以 null 结尾的字符串拷贝
char *buf = PyMem_RawMalloc(s.size + 1);
memcpy(buf, s.buf, s.size);
buf[s.size] = '\0';
// 解析 buf 中的字符串为长双精度浮点数
char *end = NULL;
errno = 0;
npy_longdouble longdouble_value = NumPyOS_ascii_strtold(buf, &end);
// 检查是否出现了范围错误
if (errno == ERANGE) {
/* strtold 返回正确符号的无穷大。如果警告触发失败则释放内存并跳转到失败标签 */
if (PyErr_Warn(PyExc_RuntimeWarning,
"overflow encountered in conversion from string") < 0) {
PyMem_RawFree(buf);
goto fail;
}
}
// 检查是否出现了其他错误或者未成功解析完整个字符串
else if (errno || end == buf || *end) {
PyErr_Format(PyExc_ValueError,
"invalid literal for long double: %s (%s)",
buf,
strerror(errno));
PyMem_RawFree(buf);
goto fail;
}
PyMem_RawFree(buf); // 释放 buf 的内存
*out = longdouble_value; // 将解析得到的长双精度浮点数存入 out 指向的位置
// 更新输入和输出指针位置
in += in_stride;
out += out_stride;
}
// 释放分配器所使用的字符串分配器
NpyString_release_allocator(allocator);
return 0;
fail:
// 释放字符串分配器,并返回-1表示失败
NpyString_release_allocator(allocator);
return -1;
}
static PyType_Slot s2ld_slots[] = {
{NPY_METH_resolve_descriptors, &string_to_longdouble_resolve_descriptors},
{NPY_METH_strided_loop, &string_to_longdouble},
{0, NULL}
};
static char *s2ld_name = "cast_StringDType_to_longdouble";
// 将 longdouble 转换为字符串
// TODO: 这里是不正确的。longdouble 到 unicode 的转换也有同样的问题。要修复这个问题,我们需要在 NumPy 中实现 ldtoa 函数。它并不在标准库中。另一个选项是使用 `snprintf`,但我们需要预先计算结果字符串的大小。
FLOAT_TO_STRING_CAST(longdouble, ld, npy_longdouble)
// 将字符串转换为 cfloat
static PyObject*
string_to_pycomplex(char *in, int has_null,
const npy_static_string *default_string,
npy_string_allocator *allocator)
{
PyObject *val_obj = non_nullable_string_to_pystring(
in, has_null, default_string, allocator);
if (val_obj == NULL) {
return NULL;
}
PyObject *args = PyTuple_Pack(1, val_obj);
Py_DECREF(val_obj);
if (args == NULL) {
return NULL;
}
PyObject *pycomplex_value = PyComplex_Type.tp_new(&PyComplex_Type, args, NULL);
Py_DECREF(args);
return pycomplex_value;
}
fail: \
// 释放字符串分配器,并返回-1表示失败
NpyString_release_allocator(allocator); \
return -1; \
} \
\
static PyType_Slot s2
{NPY_METH_resolve_descriptors, \
&string_to_
{NPY_METH_strided_loop, &string_to_
{0, NULL}}; \
\
static char *s2
STRING_TO_FLOAT_RESOLVE_DESCRIPTORS(cfloat, CFLOAT)
STRING_TO_CFLOAT_CAST(cfloat, f, float)
// 将 cfloat 转换为字符串
FLOAT_TO_STRING_CAST(cfloat, cfloat, npy_cfloat)
// 将字符串转换为 cdouble
STRING_TO_FLOAT_RESOLVE_DESCRIPTORS(cdouble, CDOUBLE)
STRING_TO_CFLOAT_CAST(cdouble, , double)
// 将 cdouble 转换为字符串
FLOAT_TO_STRING_CAST(cdouble, cdouble, npy_cdouble)
// 将字符串转换为 clongdouble
STRING_TO_FLOAT_RESOLVE_DESCRIPTORS(clongdouble, CLONGDOUBLE)
STRING_TO_CFLOAT_CAST(clongdouble, l, longdouble)
// 将 longdouble 转换为字符串
FLOAT_TO_STRING_CAST(clongdouble, clongdouble, npy_clongdouble)
// 将字符串转换为 datetime
static NPY_CASTING
// 如果给定描述符的第二个元素为NULL,表示没有指定时间单位,抛出类型错误异常并返回-1
string_to_datetime_timedelta_resolve_descriptors(
PyObject *NPY_UNUSED(self), PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]),
PyArray_Descr *given_descrs[2], PyArray_Descr *loop_descrs[2],
npy_intp *NPY_UNUSED(view_offset))
{
if (given_descrs[1] == NULL) {
PyErr_SetString(PyExc_TypeError,
"Casting from StringDType to datetimes without a unit "
"is not currently supported");
return (NPY_CASTING)-1;
}
else {
// 增加给定描述符的引用计数,并将其赋值给循环描述符的第二个元素
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
}
// 增加给定描述符的引用计数,并将其赋值给循环描述符的第一个元素
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
// 返回安全类型转换标志
return NPY_UNSAFE_CASTING;
}
// numpy将空字符串和字符串'nat'的任何大小写组合视为字符串转换中NaT的等价形式
static int
is_nat_string(const npy_static_string *s) {
// 如果字符串长度为0或者长度为3且内容为'nat'(不区分大小写),则返回真
return s->size == 0 || (s->size == 3 &&
NumPyOS_ascii_tolower(s->buf[0]) == 'n' &&
NumPyOS_ascii_tolower(s->buf[1]) == 'a' &&
NumPyOS_ascii_tolower(s->buf[2]) == 't');
}
// 将字符串转换为日期时间
static int
string_to_datetime(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取描述符对象
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 检查是否存在空值对象
int has_null = descr->na_object != NULL;
// 检查是否有字符串NA
int has_string_na = descr->has_string_na;
// 默认字符串
const npy_static_string *default_string = &descr->default_string;
// 获取第一维度大小
npy_intp N = dimensions[0];
// 输入数据指针
char *in = data[0];
// 输出数据指针(日期时间类型)
npy_datetime *out = (npy_datetime *)data[1];
// 输入数据步长
npy_intp in_stride = strides[0];
// 输出数据步长(转换为日期时间类型)
npy_intp out_stride = strides[1] / sizeof(npy_datetime);
// 日期时间结构体
npy_datetimestruct dts;
// 输入单位
NPY_DATETIMEUNIT in_unit = -1;
// 输入元数据
PyArray_DatetimeMetaData in_meta = {0, 1};
// 输出是否特殊值
npy_bool out_special;
// 获取日期时间描述符对象
_PyArray_LegacyDescr *dt_descr = (_PyArray_LegacyDescr *)context->descriptors[1];
// 获取日期时间元数据
PyArray_DatetimeMetaData *dt_meta =
&(((PyArray_DatetimeDTypeMetaData *)dt_descr->c_metadata)->meta);
while (N--) {
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
npy_static_string s = {0, NULL};
int is_null = NpyString_load(allocator, ps, &s);
if (is_null == -1) {
PyErr_SetString(
PyExc_MemoryError,
"Failed to load string in string to datetime cast");
goto fail;
}
if (is_null) {
if (has_null && !has_string_na) {
*out = NPY_DATETIME_NAT;
goto next_step;
}
s = *default_string;
}
if (is_nat_string(&s)) {
*out = NPY_DATETIME_NAT;
goto next_step;
}
// 实际解析日期时间字符串
// 调用 NpyDatetime_ParseISO8601Datetime 函数解析 ISO 8601 格式的日期时间字符串
if (NpyDatetime_ParseISO8601Datetime(
(const char *)s.buf, s.size, in_unit, NPY_UNSAFE_CASTING,
&dts, &in_meta.base, &out_special) < 0) {
goto fail;
}
// 将日期时间结构转换为 datetime64 类型
if (NpyDatetime_ConvertDatetimeStructToDatetime64(dt_meta, &dts, out) <
0) {
goto fail;
}
next_step:
in += in_stride;
out += out_stride;
}
NpyString_release_allocator(allocator);
return 0;
// 释放内存分配器并返回错误代码
fail:
NpyString_release_allocator(allocator);
return -1;
}
// 定义 PyType_Slot 结构数组,描述 s2dt 对象的行为
static PyType_Slot s2dt_slots[] = {
// 解析描述符方法
{NPY_METH_resolve_descriptors,
&string_to_datetime_timedelta_resolve_descriptors},
// 循环处理方法
{NPY_METH_strided_loop, &string_to_datetime},
// 数组结束标志
{0, NULL}};
// 字符串变换为日期时间的名称
static char *s2dt_name = "cast_StringDType_to_Datetime";
// datetime 转换为字符串
static int
datetime_to_string(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取数组维度
npy_intp N = dimensions[0];
// 输入数据的指针
npy_datetime *in = (npy_datetime *)data[0];
// 输出数据的指针
char *out = data[1];
// 输入数据的步幅
npy_intp in_stride = strides[0] / sizeof(npy_datetime);
// 输出数据的步幅
npy_intp out_stride = strides[1];
// 获取日期时间描述符
_PyArray_LegacyDescr *dt_descr = (_PyArray_LegacyDescr *)context->descriptors[0];
// 获取日期时间元数据
PyArray_DatetimeMetaData *dt_meta =
&(((PyArray_DatetimeDTypeMetaData *)dt_descr->c_metadata)->meta);
// 用于构建日期时间字符串的缓冲区
char datetime_buf[NPY_DATETIME_MAX_ISO8601_STRLEN];
// 获取字符串描述符
PyArray_StringDTypeObject *sdescr = (PyArray_StringDTypeObject *)context->descriptors[1];
// 检查是否有空值对象
int has_null = sdescr->na_object != NULL;
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(sdescr);
while (N--) {
npy_packed_static_string *out_pss = (npy_packed_static_string *)out;
// 如果输入值为 NaT (Not a Time),处理特殊情况
if (*in == NPY_DATETIME_NAT)
{
// 如果没有空值,将字符串 "NaT" 打包到输出结构中
if (!has_null) {
npy_static_string os = {3, "NaT"};
// 调用 NpyString_pack 函数尝试将 "NaT" 打包到输出结构
if (NpyString_pack(allocator, out_pss, os.buf, os.size) < 0) {
// 若失败,抛出内存错误并跳转到失败处理标签
npy_gil_error(
PyExc_MemoryError,
"Failed to pack string in datetime to string "
"cast");
goto fail;
}
}
// 如果有空值,调用 NpyString_pack_null 函数将空值打包到输出结构
else if (NpyString_pack_null(allocator, out_pss) < 0) {
// 若失败,抛出内存错误并跳转到失败处理标签
npy_gil_error(
PyExc_MemoryError,
"Failed to pack string in datetime to string cast");
goto fail;
}
}
// 对于正常日期时间值,进行格式转换并打包到输出结构
else {
npy_datetimestruct dts;
// 将输入日期时间值转换为日期时间结构
if (NpyDatetime_ConvertDatetime64ToDatetimeStruct(
dt_meta, *in, &dts) < 0) {
// 若转换失败,跳转到失败处理标签
goto fail;
}
// 将日期时间缓冲区清零
memset(datetime_buf, 0, NPY_DATETIME_MAX_ISO8601_STRLEN);
// 将日期时间结构转换为 ISO8601 格式的字符串
if (NpyDatetime_MakeISO8601Datetime(
&dts, datetime_buf, NPY_DATETIME_MAX_ISO8601_STRLEN, 0,
0, dt_meta->base, -1, NPY_UNSAFE_CASTING) < 0) {
// 若转换失败,跳转到失败处理标签
goto fail;
}
// 将转换后的 ISO8601 格式字符串打包到输出结构
if (NpyString_pack(allocator, out_pss, datetime_buf,
strlen(datetime_buf)) < 0) {
// 若打包失败,设置异常并跳转到失败处理标签
PyErr_SetString(PyExc_MemoryError,
"Failed to pack string while converting "
"from a datetime.");
goto fail;
}
}
// 更新输入和输出指针位置
in += in_stride;
out += out_stride;
}
// 释放分配器资源
NpyString_release_allocator(allocator);
// 返回成功状态
return 0;
fail:
// 释放字符串分配器的资源
NpyString_release_allocator(allocator);
// 返回-1,表示函数执行失败
return -1;
}
static PyType_Slot dt2s_slots[] = {
// 方法插槽:解析描述符,使用安全版本的任意类型到字符串转换方法
{NPY_METH_resolve_descriptors,
&any_to_string_SAFE_resolve_descriptors},
// 方法插槽:使用日期时间对象进行跨步循环的字符串转换
{NPY_METH_strided_loop, &datetime_to_string},
{0, NULL}};
static char *dt2s_name = "cast_Datetime_to_StringDType";
// string to timedelta
static int
string_to_timedelta(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取字符串数据类型的描述符对象
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 判断是否有空值对象
int has_null = descr->na_object != NULL;
// 判断是否包含字符串 NA
int has_string_na = descr->has_string_na;
// 默认字符串
const npy_static_string *default_string = &descr->default_string;
// 数据维度
npy_intp N = dimensions[0];
// 输入数据指针
char *in = data[0];
// 输出数据指针
npy_timedelta *out = (npy_timedelta *)data[1];
// 输入和输出的步长
npy_intp in_stride = strides[0];
npy_intp out_stride = strides[1] / sizeof(npy_timedelta);
// 循环处理每个元素
while (N--) {
// 转换为紧凑静态字符串对象
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
// 静态字符串对象
npy_static_string s = {0, NULL};
// 加载字符串并检查是否为空
int is_null = NpyString_load(allocator, ps, &s);
// 如果加载失败,设置内存错误并跳转到错误处理标签
if (is_null == -1) {
PyErr_SetString(
PyExc_MemoryError,
"Failed to load string in string to datetime cast");
goto fail;
}
// 如果是空字符串
if (is_null) {
// 如果允许空值且不包含字符串 NA,设置为默认字符串
if (has_null && !has_string_na) {
*out = NPY_DATETIME_NAT;
goto next_step;
}
s = *default_string;
}
// 如果是 NA 字符串
if (is_nat_string(&s)) {
*out = NPY_DATETIME_NAT;
goto next_step;
}
// 从静态字符串创建 Python Unicode 对象
PyObject *pystr = PyUnicode_FromStringAndSize(s.buf, s.size);
if (pystr == NULL) {
goto fail;
}
// 解释为十进制整数
PyObject *pylong_value = PyLong_FromUnicodeObject(pystr, 10);
Py_DECREF(pystr);
if (pylong_value == NULL) {
goto fail;
}
// 将 Python 长整型对象转换为 C 长整型
npy_longlong value = PyLong_AsLongLong(pylong_value);
Py_DECREF(pylong_value);
if (value == -1 && PyErr_Occurred()) {
goto fail;
}
// 将结果赋给输出的 timedelta 数组
*out = (npy_timedelta)value;
next_step:
// 移动输入和输出指针到下一个元素
in += in_stride;
out += out_stride;
}
// 释放字符串分配器的资源
NpyString_release_allocator(allocator);
// 返回成功
return 0;
fail:
// 释放字符串分配器的资源
NpyString_release_allocator(allocator);
// 返回失败
return -1;
}
static PyType_Slot s2td_slots[] = {
// 方法插槽:解析描述符,使用字符串到日期时间类型转换的描述符
{NPY_METH_resolve_descriptors,
&string_to_datetime_timedelta_resolve_descriptors},
// 方法插槽:使用字符串到 timedelta 类型的转换方法
{NPY_METH_strided_loop, &string_to_timedelta},
{0, NULL}};
static char *s2td_name = "cast_StringDType_to_Timedelta";
// timedelta to string
static int
// 将 timedelta 转换为字符串
timedelta_to_string(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取第一个维度的大小
npy_intp N = dimensions[0];
// 获取输入数据指针,并转换为 npy_timedelta 类型
npy_timedelta *in = (npy_timedelta *)data[0];
// 获取输出数据指针
char *out = data[1];
// 计算输入数据的步长和输出数据的步长
npy_intp in_stride = strides[0] / sizeof(npy_timedelta);
npy_intp out_stride = strides[1];
// 获取字符串类型描述符
PyArray_StringDTypeObject *sdescr = (PyArray_StringDTypeObject *)context->descriptors[1];
// 检查是否有空值对象
int has_null = sdescr->na_object != NULL;
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(sdescr);
// 循环处理每个元素
while (N--) {
// 输出数据的静态字符串指针
npy_packed_static_string *out_pss = (npy_packed_static_string *)out;
// 如果输入是 NaT(Not a Time),处理特殊情况
if (*in == NPY_DATETIME_NAT)
{
// 如果没有空值对象,将 "NaT" 打包到输出静态字符串中
if (!has_null) {
npy_static_string os = {3, "NaT"};
// 使用字符串分配器打包字符串到输出静态字符串指针中
if (NpyString_pack(allocator, out_pss, os.buf, os.size) < 0) {
// 如果打包失败,抛出内存错误并跳转到失败处理标签
npy_gil_error(
PyExc_MemoryError,
"Failed to pack string in timedelta to string "
"cast");
goto fail;
}
}
// 如果有空值对象,使用 NpyString_pack_null 函数打包空值对象
else if (NpyString_pack_null(allocator, out_pss) < 0) {
npy_gil_error(
PyExc_MemoryError,
"Failed to pack string in timedelta to string cast");
goto fail;
}
}
// 如果输入不是 NaT,则将其转换为字符串并存储到输出中
else if (int_to_stringbuf((long long)*in, out, allocator) < 0) {
// 如果转换失败,跳转到失败处理标签
goto fail;
}
// 更新输入和输出指针
in += in_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocator(allocator);
// 返回成功标志
return 0;
fail:
// 失败时释放字符串分配器并返回错误标志
NpyString_release_allocator(allocator);
return -1;
}
// timedelta 转换为字符串的方法槽
static PyType_Slot td2s_slots[] = {
{NPY_METH_resolve_descriptors,
&any_to_string_SAFE_resolve_descriptors},
{NPY_METH_strided_loop, &timedelta_to_string},
{0, NULL}};
// timedelta 转换为字符串的名称
static char *td2s_name = "cast_Timedelta_to_StringDType";
// 字符串到空值的方法:解析描述符
static NPY_CASTING
string_to_void_resolve_descriptors(PyObject *NPY_UNUSED(self),
PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]),
PyArray_Descr *given_descrs[2],
PyArray_Descr *loop_descrs[2],
npy_intp *NPY_UNUSED(view_offset))
{
// 如果给定的描述符为空,抛出类型错误并返回错误标志
if (given_descrs[1] == NULL) {
PyErr_SetString(
PyExc_TypeError,
"Casting from StringDType to a fixed-width dtype with an "
"unspecified size is not currently supported, specify "
"an explicit size for the output dtype instead.");
return (NPY_CASTING)-1;
}
这些注释解释了每行代码的作用和功能,按照要求将其包含在代码块中。
else {
// 如果给定描述符的类型为结构化空类型,则拒绝
if (PyDataType_NAMES(given_descrs[1]) != NULL || PyDataType_SUBARRAY(given_descrs[1]) != NULL) {
// 设置类型错误异常,说明从 StringDType 转换到结构化 dtype 是不支持的
PyErr_SetString(
PyExc_TypeError,
"Casting from StringDType to a structured dtype is not "
"supported.");
// 返回错误码表示转换失败
return (NPY_CASTING)-1;
}
// 增加给定描述符的引用计数,确保其在返回前不被销毁
Py_INCREF(given_descrs[1]);
// 将给定描述符复制到循环描述符数组的相应位置
loop_descrs[1] = given_descrs[1];
}
// 增加给定描述符的引用计数,确保其在返回前不被销毁
Py_INCREF(given_descrs[0]);
// 将给定描述符复制到循环描述符数组的相应位置
loop_descrs[0] = given_descrs[0];
// 返回不安全转换的标志,表示允许不安全的类型转换
return NPY_UNSAFE_CASTING;
}
static int
string_to_void(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取输入描述符的字符串数据类型对象
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 检查是否有空值对象
int has_null = descr->na_object != NULL;
// 检查是否有字符串NA值
int has_string_na = descr->has_string_na;
// 获取默认字符串
const npy_static_string *default_string = &descr->default_string;
// 获取NA名称字符串
const npy_static_string *na_name = &descr->na_name;
// 获取第一维度的大小
npy_intp N = dimensions[0];
// 获取输入数据的指针
char *in = data[0];
// 获取输出数据的指针
char *out = data[1];
// 获取输入和输出数据的步幅
npy_intp in_stride = strides[0];
npy_intp out_stride = strides[1];
// 获取输出数据的最大大小
size_t max_out_size = context->descriptors[1]->elsize;
// 遍历输入数据
while (N--) {
// 将输入数据解析为可空字符串
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
npy_static_string s = {0, NULL};
// 加载可空字符串,处理可能的截断或错误情况
if (load_nullable_string(ps, &s, has_null, has_string_na,
default_string, na_name, allocator,
"in string to void cast") == -1) {
// 加载失败,跳转到错误处理部分
goto fail;
}
// 将字符串数据复制到输出位置,可能会截断UTF-8字符
memcpy(out, s.buf, s.size > max_out_size ? max_out_size : s.size);
// 如果实际字符串大小小于输出大小,用零填充剩余部分
if (s.size < max_out_size) {
memset(out + s.size, 0, (max_out_size - s.size));
}
// 更新输入和输出指针位置
in += in_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocator(allocator);
// 成功完成,返回0
return 0;
fail:
// 处理失败,释放字符串分配器并返回-1
NpyString_release_allocator(allocator);
return -1;
}
static PyType_Slot s2v_slots[] = {
// 解析描述符的方法和函数指针
{NPY_METH_resolve_descriptors, &string_to_void_resolve_descriptors},
// 字符串到空值的函数指针
{NPY_METH_strided_loop, &string_to_void},
// 结束标记
{0, NULL}
};
static char *s2v_name = "cast_StringDType_to_Void";
// 空值到字符串
static int
void_to_string(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取描述符数组
PyArray_Descr *const *descrs = context->descriptors;
// 获取输出描述符的字符串数据类型对象
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)descrs[1];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 获取输入描述符的最大大小
long max_in_size = descrs[0]->elsize;
// 获取第一维度的大小
npy_intp N = dimensions[0];
// 获取输入数据的指针(无符号字符指针)
unsigned char *in = (unsigned char *)data[0];
// 获取输出数据的指针
char *out = data[1];
// 获取输入和输出数据的步幅
npy_intp in_stride = strides[0];
npy_intp out_stride = strides[1];
// 循环,执行 N 次,N 是循环控制变量
while(N--) {
// 计算输入数据的 UTF-8 编码后的字节数
size_t out_num_bytes = utf8_buffer_size(in, max_in_size);
// 如果计算出的字节数小于 0,表示发现无效的 UTF-8 字节序列,抛出错误并跳转到失败标签
if (out_num_bytes < 0) {
npy_gil_error(PyExc_TypeError,
"Invalid UTF-8 bytes found, cannot convert to UTF-8");
goto fail;
}
// 定义一个静态字符串结构体 out_ss,并初始化为零
npy_static_string out_ss = {0, NULL};
// 调用 load_new_string 函数,将 out 转换为字符串,存储在 out_ss 中,使用 allocator 分配内存,描述为 "void to string cast"
if (load_new_string((npy_packed_static_string *)out,
&out_ss, out_num_bytes, allocator,
"void to string cast") == -1) {
// 如果 load_new_string 返回 -1,表示转换失败,跳转到失败标签
goto fail;
}
// 将输入数据 in 复制到 out_buf 中,长度为 out_num_bytes
char *out_buf = (char *)out_ss.buf;
memcpy(out_buf, in, out_num_bytes);
// 更新输入指针和输出指针的位置
in += in_stride;
out += out_stride;
}
// 释放 allocator 分配的内存资源
NpyString_release_allocator(allocator);
// 返回 0 表示成功
return 0;
// 释放内存分配器
NpyString_release_allocator(allocator);
// 返回-1,表示函数执行失败
return -1;
}
// 定义 PyType_Slot 结构体数组 v2s_slots,用于描述一个 Python 类型的方法和数据
static PyType_Slot v2s_slots[] = {{NPY_METH_resolve_descriptors,
&any_to_string_SAME_KIND_resolve_descriptors},
{NPY_METH_strided_loop, &void_to_string},
{0, NULL}};
// 字符串变为字节码
// static int 修饰符表明这是一个静态函数,只在当前文件可见
static int
string_to_bytes(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 从上下文中获取字符串类型描述符
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 检查是否有空值对象
int has_null = descr->na_object != NULL;
// 检查是否有字符串 NA
int has_string_na = descr->has_string_na;
// 获取默认字符串
const npy_static_string *default_string = &descr->default_string;
// 获取 NA 名称
const npy_static_string *na_name = &descr->na_name;
// 获取第一个维度的大小
npy_intp N = dimensions[0];
// 获取输入数据的指针
char *in = data[0];
// 获取输出数据的指针
char *out = data[1];
// 获取输入数据的步幅
npy_intp in_stride = strides[0];
// 获取输出数据的步幅
npy_intp out_stride = strides[1];
// 获取输出数据的最大大小
size_t max_out_size = context->descriptors[1]->elsize;
// 迭代处理每一个输入字符串
while (N--) {
// 将输入数据解析为紧凑字符串结构体
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
// 创建静态字符串结构体 s
npy_static_string s = {0, NULL};
// 加载可空字符串,如果加载失败则跳转到失败标签
if (load_nullable_string(ps, &s, has_null, has_string_na,
default_string, na_name, allocator,
"in string to bytes cast") == -1) {
goto fail;
}
// 检查字符串中是否有超过 127 的 ASCII 字符
for (size_t i=0; i<s.size; i++) {
if (((unsigned char *)s.buf)[i] > 127) {
// 异常处理:ASCII 转换错误
NPY_ALLOW_C_API_DEF;
NPY_ALLOW_C_API;
// 创建并设置 UnicodeEncodeError 异常对象
PyObject *exc = PyObject_CallFunction(
PyExc_UnicodeEncodeError, "ss#nns", "ascii", s.buf,
(Py_ssize_t)s.size, (Py_ssize_t)i, (Py_ssize_t)(i+1), "ordinal not in range(128)");
PyErr_SetObject(PyExceptionInstance_Class(exc), exc);
Py_DECREF(exc);
NPY_DISABLE_C_API;
// 转换失败,跳转到失败标签
goto fail;
}
}
// 复制字符串数据到输出缓冲区,不超过最大输出大小
memcpy(out, s.buf, s.size > max_out_size ? max_out_size : s.size);
// 如果字符串大小小于最大输出大小,填充剩余空间
if (s.size < max_out_size) {
memset(out + s.size, 0, (max_out_size - s.size));
}
// 更新输入和输出指针
in += in_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocator(allocator);
// 返回成功
return 0;
// 失败处理标签
fail:
// 释放字符串分配器
NpyString_release_allocator(allocator);
// 返回 -1,表示函数执行失败
return -1;
}
// 定义 PyType_Slot 结构体数组 s2bytes_slots,用于描述一个 Python 类型的方法和数据
static PyType_Slot s2bytes_slots[] = {
{NPY_METH_resolve_descriptors, &string_to_fixed_width_resolve_descriptors},
{NPY_METH_strided_loop, &string_to_bytes},
{0, NULL}};
// 字节码变为字符串
// static int 修饰符表明这是一个静态函数,只在当前文件可见
static int
// 将字节转换为字符串的函数,使用给定的上下文、数据、维度、步长和辅助数据
static void bytes_to_string(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取描述符数组
PyArray_Descr *const *descrs = context->descriptors;
// 获取第二个描述符作为字符串描述符
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)descrs[1];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 获取最大输入大小
size_t max_in_size = descrs[0]->elsize;
// 获取第一个维度
npy_intp N = dimensions[0];
// 输入数据的无符号字符指针
unsigned char *in = (unsigned char *)data[0];
// 输出数据的字符指针
char *out = data[1];
// 输入和输出的步长
npy_intp in_stride = strides[0];
npy_intp out_stride = strides[1];
// 循环处理每一个元素
while(N--) {
// 初始化输出字节数
size_t out_num_bytes = max_in_size;
// 忽略末尾的空字符
while (out_num_bytes > 0 && in[out_num_bytes - 1] == 0) {
out_num_bytes--;
}
// 定义静态字符串结构体
npy_static_string out_ss = {0, NULL};
// 调用加载新字符串的函数,将输入转换为输出静态字符串
if (load_new_string((npy_packed_static_string *)out,
&out_ss, out_num_bytes, allocator,
"void to string cast") == -1) {
// 失败时跳转到标签fail
goto fail;
}
// 将输入复制到输出缓冲区
char *out_buf = (char *)out_ss.buf;
memcpy(out_buf, in, out_num_bytes);
// 更新输入和输出指针
in += in_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocator(allocator);
return;
fail:
// 失败时同样释放字符串分配器,并返回-1
NpyString_release_allocator(allocator);
return;
}
// 定义静态类型槽数组,用于描述类型转换的方法
static PyType_Slot bytes2s_slots[] = {
// 解析描述符的方法和解析相同类型的字符串描述符的方法
{NPY_METH_resolve_descriptors, &any_to_string_SAME_KIND_resolve_descriptors},
// 使用字节转换为字符串的方法
{NPY_METH_strided_loop, &bytes_to_string},
// 空槽
{0, NULL}
};
// 定义将字节转换为字符串的名称
static char *bytes2s_name = "cast_Bytes_to_StringDType";
// 返回描述类型转换的方法规范的函数
PyArrayMethod_Spec *
get_cast_spec(const char *name, NPY_CASTING casting,
NPY_ARRAYMETHOD_FLAGS flags, PyArray_DTypeMeta **dtypes,
PyType_Slot *slots)
{
// 分配内存以存储方法规范
PyArrayMethod_Spec *ret = PyMem_Malloc(sizeof(PyArrayMethod_Spec));
// 设置方法规范的属性
ret->name = name;
ret->nin = 1;
ret->nout = 1;
ret->casting = casting;
ret->flags = flags;
ret->dtypes = dtypes;
ret->slots = slots;
return ret;
}
// 返回两个数据类型元信息的数组
PyArray_DTypeMeta **
get_dtypes(PyArray_DTypeMeta *dt1, PyArray_DTypeMeta *dt2)
{
// 分配内存以存储两个数据类型元信息的数组
PyArray_DTypeMeta **ret = PyMem_Malloc(2 * sizeof(PyArray_DTypeMeta *));
// 设置数组的元素为输入的数据类型元信息
ret[0] = dt1;
ret[1] = dt2;
return ret;
}
// 返回类型转换的方法规范的数组的函数
PyArrayMethod_Spec **
get_casts()
{
// 使用字符串到字符串转换的名称
char *t2t_name = s2s_name;
// 获取字符串到字符串的数据类型元信息数组
PyArray_DTypeMeta **t2t_dtypes =
get_dtypes(&PyArray_StringDType,
&PyArray_StringDType);
// 获取字符串到字符串的方法规范
PyArrayMethod_Spec *ThisToThisCastSpec =
get_cast_spec(t2t_name, NPY_UNSAFE_CASTING,
NPY_METH_SUPPORTS_UNALIGNED, t2t_dtypes, s2s_slots);
// 初始类型转换的数量
int num_casts = 43;
// 根据字节大小添加类型转换数量
num_casts += 4;
num_casts += 4;
num_casts += 4;
num_casts += 4;
// 获取 Unicode 到 String 的数据类型元信息数组
PyArray_DTypeMeta **u2s_dtypes = get_dtypes(
&PyArray_UnicodeDType, &PyArray_StringDType);
// 获取 Unicode 到 String 类型转换方法的规范
PyArrayMethod_Spec *UnicodeToStringCastSpec = get_cast_spec(
u2s_name, NPY_SAME_KIND_CASTING, NPY_METH_NO_FLOATINGPOINT_ERRORS,
u2s_dtypes, u2s_slots);
// 获取 String 到 Unicode 的数据类型元信息数组
PyArray_DTypeMeta **s2u_dtypes = get_dtypes(
&PyArray_StringDType, &PyArray_UnicodeDType);
// 获取 String 到 Unicode 类型转换方法的规范
PyArrayMethod_Spec *StringToUnicodeCastSpec = get_cast_spec(
s2u_name, NPY_SAME_KIND_CASTING, NPY_METH_NO_FLOATINGPOINT_ERRORS,
s2u_dtypes, s2u_slots);
// 获取 String 到 Bool 的数据类型元信息数组
PyArray_DTypeMeta **s2b_dtypes =
get_dtypes(&PyArray_StringDType, &PyArray_BoolDType);
// 获取 String 到 Bool 类型转换方法的规范
PyArrayMethod_Spec *StringToBoolCastSpec = get_cast_spec(
s2b_name, NPY_SAME_KIND_CASTING, NPY_METH_NO_FLOATINGPOINT_ERRORS,
s2b_dtypes, s2b_slots);
// 获取 Bool 到 String 的数据类型元信息数组
PyArray_DTypeMeta **b2s_dtypes =
get_dtypes(&PyArray_BoolDType, &PyArray_StringDType);
// 获取 Bool 到 String 类型转换方法的规范
PyArrayMethod_Spec *BoolToStringCastSpec = get_cast_spec(
b2s_name, NPY_SAME_KIND_CASTING, NPY_METH_NO_FLOATINGPOINT_ERRORS,
b2s_dtypes, b2s_slots);
// 定义各个整数类型到相应的类型转换规范
DTYPES_AND_CAST_SPEC(i8, Int8)
DTYPES_AND_CAST_SPEC(i16, Int16)
DTYPES_AND_CAST_SPEC(i32, Int32)
DTYPES_AND_CAST_SPEC(i64, Int64)
DTYPES_AND_CAST_SPEC(u8, UInt8)
DTYPES_AND_CAST_SPEC(u16, UInt16)
DTYPES_AND_CAST_SPEC(u32, UInt32)
DTYPES_AND_CAST_SPEC(u64, UInt64)
DTYPES_AND_CAST_SPEC(byte, Byte)
DTYPES_AND_CAST_SPEC(ubyte, UByte)
DTYPES_AND_CAST_SPEC(short, Short)
DTYPES_AND_CAST_SPEC(ushort, UShort)
DTYPES_AND_CAST_SPEC(int, Int)
DTYPES_AND_CAST_SPEC(uint, UInt)
DTYPES_AND_CAST_SPEC(longlong, LongLong)
DTYPES_AND_CAST_SPEC(ulonglong, ULongLong)
// 定义各个浮点数类型到相应的类型转换规范
DTYPES_AND_CAST_SPEC(f64, Double)
DTYPES_AND_CAST_SPEC(f32, Float)
DTYPES_AND_CAST_SPEC(f16, Half)
// 获取 String 到 Datetime 的数据类型元信息数组
PyArray_DTypeMeta **s2dt_dtypes = get_dtypes(
&PyArray_StringDType, &PyArray_DatetimeDType);
// 获取 String 到 Datetime 类型转换方法的规范
PyArrayMethod_Spec *StringToDatetimeCastSpec = get_cast_spec(
s2dt_name, NPY_UNSAFE_CASTING,
NPY_METH_NO_FLOATINGPOINT_ERRORS | NPY_METH_REQUIRES_PYAPI,
s2dt_dtypes, s2dt_slots);
// 获取 Datetime 到 String 的数据类型元信息数组
PyArray_DTypeMeta **dt2s_dtypes = get_dtypes(
&PyArray_DatetimeDType, &PyArray_StringDType);
// 获取 Datetime 到 String 类型转换方法的规范
PyArrayMethod_Spec *DatetimeToStringCastSpec = get_cast_spec(
dt2s_name, NPY_SAFE_CASTING,
NPY_METH_NO_FLOATINGPOINT_ERRORS | NPY_METH_REQUIRES_PYAPI,
dt2s_dtypes, dt2s_slots);
// 获取 String 到 Timedelta 的数据类型元信息数组
PyArray_DTypeMeta **s2td_dtypes = get_dtypes(
&PyArray_StringDType, &PyArray_TimedeltaDType);
// 创建 StringToTimedeltaCastSpec 变量,并使用 get_cast_spec 函数获取字符串到时间增量的转换规格
PyArrayMethod_Spec *StringToTimedeltaCastSpec = get_cast_spec(
s2td_name, NPY_UNSAFE_CASTING,
NPY_METH_NO_FLOATINGPOINT_ERRORS | NPY_METH_REQUIRES_PYAPI,
s2td_dtypes, s2td_slots);
// 使用 get_dtypes 函数获取 PyArray_TimedeltaDType 和 PyArray_StringDType 的数据类型元信息,存储在 td2s_dtypes 中
PyArray_DTypeMeta **td2s_dtypes = get_dtypes(
&PyArray_TimedeltaDType, &PyArray_StringDType);
// 创建 TimedeltaToStringCastSpec 变量,并使用 get_cast_spec 函数获取时间增量到字符串的转换规格
PyArrayMethod_Spec *TimedeltaToStringCastSpec = get_cast_spec(
td2s_name, NPY_SAFE_CASTING,
NPY_METH_NO_FLOATINGPOINT_ERRORS | NPY_METH_REQUIRES_PYAPI,
td2s_dtypes, td2s_slots);
// 使用 get_dtypes 函数获取 PyArray_StringDType 和 PyArray_LongDoubleDType 的数据类型元信息,存储在 s2ld_dtypes 中
PyArray_DTypeMeta **s2ld_dtypes = get_dtypes(
&PyArray_StringDType, &PyArray_LongDoubleDType);
// 创建 StringToLongDoubleCastSpec 变量,并使用 get_cast_spec 函数获取字符串到长双精度浮点数的转换规格
PyArrayMethod_Spec *StringToLongDoubleCastSpec = get_cast_spec(
s2ld_name, NPY_UNSAFE_CASTING, NPY_METH_NO_FLOATINGPOINT_ERRORS,
s2ld_dtypes, s2ld_slots);
// 使用 get_dtypes 函数获取 PyArray_LongDoubleDType 和 PyArray_StringDType 的数据类型元信息,存储在 ld2s_dtypes 中
PyArray_DTypeMeta **ld2s_dtypes = get_dtypes(
&PyArray_LongDoubleDType, &PyArray_StringDType);
// 创建 LongDoubleToStringCastSpec 变量,并使用 get_cast_spec 函数获取长双精度浮点数到字符串的转换规格
PyArrayMethod_Spec *LongDoubleToStringCastSpec = get_cast_spec(
ld2s_name, NPY_SAFE_CASTING,
NPY_METH_NO_FLOATINGPOINT_ERRORS | NPY_METH_REQUIRES_PYAPI,
ld2s_dtypes, ld2s_slots);
// 调用宏 DTYPES_AND_CAST_SPEC 生成有关复数浮点数到浮点数的转换规格代码
DTYPES_AND_CAST_SPEC(cfloat, CFloat)
DTYPES_AND_CAST_SPEC(cdouble, CDouble)
DTYPES_AND_CAST_SPEC(clongdouble, CLongDouble)
// 使用 get_dtypes 函数获取 PyArray_StringDType 和 PyArray_VoidDType 的数据类型元信息,存储在 s2v_dtypes 中
PyArray_DTypeMeta **s2v_dtypes = get_dtypes(
&PyArray_StringDType, &PyArray_VoidDType);
// 创建 StringToVoidCastSpec 变量,并使用 get_cast_spec 函数获取字符串到 void 类型的转换规格
PyArrayMethod_Spec *StringToVoidCastSpec = get_cast_spec(
s2v_name, NPY_SAME_KIND_CASTING, NPY_METH_NO_FLOATINGPOINT_ERRORS,
s2v_dtypes, s2v_slots);
// 使用 get_dtypes 函数获取 PyArray_VoidDType 和 PyArray_StringDType 的数据类型元信息,存储在 v2s_dtypes 中
PyArray_DTypeMeta **v2s_dtypes = get_dtypes(
&PyArray_VoidDType, &PyArray_StringDType);
// 创建 VoidToStringCastSpec 变量,并使用 get_cast_spec 函数获取 void 类型到字符串的转换规格
PyArrayMethod_Spec *VoidToStringCastSpec = get_cast_spec(
v2s_name, NPY_SAME_KIND_CASTING, NPY_METH_NO_FLOATINGPOINT_ERRORS,
v2s_dtypes, v2s_slots);
// 使用 get_dtypes 函数获取 PyArray_StringDType 和 PyArray_BytesDType 的数据类型元信息,存储在 s2bytes_dtypes 中
PyArray_DTypeMeta **s2bytes_dtypes = get_dtypes(
&PyArray_StringDType, &PyArray_BytesDType);
// 创建 StringToBytesCastSpec 变量,并使用 get_cast_spec 函数获取字符串到字节流的转换规格
PyArrayMethod_Spec *StringToBytesCastSpec = get_cast_spec(
s2bytes_name, NPY_SAME_KIND_CASTING, NPY_METH_NO_FLOATINGPOINT_ERRORS,
s2bytes_dtypes, s2bytes_slots);
// 使用 get_dtypes 函数获取 PyArray_BytesDType 和 PyArray_StringDType 的数据类型元信息,存储在 bytes2s_dtypes 中
PyArray_DTypeMeta **bytes2s_dtypes = get_dtypes(
&PyArray_BytesDType, &PyArray_StringDType);
// 创建 BytesToStringCastSpec 变量,并使用 get_cast_spec 函数获取字节流到字符串的转换规格
PyArrayMethod_Spec *BytesToStringCastSpec = get_cast_spec(
bytes2s_name, NPY_SAME_KIND_CASTING, NPY_METH_NO_FLOATINGPOINT_ERRORS,
bytes2s_dtypes, bytes2s_slots);
// 分配内存以存储所有转换规格的数组,并将指针存储在 casts 中
PyArrayMethod_Spec **casts =
PyMem_Malloc((num_casts + 1) * sizeof(PyArrayMethod_Spec *));
// 初始化计数器 cast_i
int cast_i = 0;
// 将各种转换规格添加到 casts 数组中
casts[cast_i++] = ThisToThisCastSpec;
casts[cast_i++] = UnicodeToStringCastSpec;
casts[cast_i++] = StringToUnicodeCastSpec;
casts[cast_i++] = StringToBoolCastSpec;
casts[cast_i++] = BoolToStringCastSpec;
casts[cast_i++] = StringToInt8CastSpec;
casts[cast_i++] = Int8ToStringCastSpec;
casts[cast_i++] = StringToInt16CastSpec;
casts[cast_i++] = Int16ToStringCastSpec;
casts[cast_i++] = StringToInt32CastSpec;
casts[cast_i++] = Int32ToStringCastSpec;
casts[cast_i++] = StringToInt64CastSpec;
casts[cast_i++] = Int64ToStringCastSpec;
casts[cast_i++] = StringToUInt8CastSpec;
casts[cast_i++] = UInt8ToStringCastSpec;
casts[cast_i++] = StringToUInt16CastSpec;
casts[cast_i++] = UInt16ToStringCastSpec;
casts[cast_i++] = StringToUInt32CastSpec;
casts[cast_i++] = UInt32ToStringCastSpec;
casts[cast_i++] = StringToUInt64CastSpec;
casts[cast_i++] = UInt64ToStringCastSpec;
// 如果字节大小等于短整型大小,添加字节到短整型的类型转换函数到 casts 数组中
casts[cast_i++] = StringToByteCastSpec;
casts[cast_i++] = ByteToStringCastSpec;
casts[cast_i++] = StringToUByteCastSpec;
casts[cast_i++] = UByteToStringCastSpec;
// 如果短整型大小等于整型大小,添加短整型到整型的类型转换函数到 casts 数组中
casts[cast_i++] = StringToShortCastSpec;
casts[cast_i++] = ShortToStringCastSpec;
casts[cast_i++] = StringToUShortCastSpec;
casts[cast_i++] = UShortToStringCastSpec;
// 如果整型大小等于长整型大小,添加整型到长整型的类型转换函数到 casts 数组中
casts[cast_i++] = StringToIntCastSpec;
casts[cast_i++] = IntToStringCastSpec;
casts[cast_i++] = StringToUIntCastSpec;
casts[cast_i++] = UIntToStringCastSpec;
// 如果长长整型大小等于长整型大小,添加长长整型到长整型的类型转换函数到 casts 数组中
casts[cast_i++] = StringToLongLongCastSpec;
casts[cast_i++] = LongLongToStringCastSpec;
casts[cast_i++] = StringToULongLongCastSpec;
casts[cast_i++] = ULongLongToStringCastSpec;
// 添加字符串到双精度浮点数的类型转换函数到 casts 数组中
casts[cast_i++] = StringToDoubleCastSpec;
casts[cast_i++] = DoubleToStringCastSpec;
// 添加字符串到单精度浮点数的类型转换函数到 casts 数组中
casts[cast_i++] = StringToFloatCastSpec;
casts[cast_i++] = FloatToStringCastSpec;
// 添加字符串到半精度浮点数的类型转换函数到 casts 数组中
casts[cast_i++] = StringToHalfCastSpec;
casts[cast_i++] = HalfToStringCastSpec;
// 添加字符串到日期时间的类型转换函数到 casts 数组中
casts[cast_i++] = StringToDatetimeCastSpec;
casts[cast_i++] = DatetimeToStringCastSpec;
// 添加字符串到时间增量的类型转换函数到 casts 数组中
casts[cast_i++] = StringToTimedeltaCastSpec;
casts[cast_i++] = TimedeltaToStringCastSpec;
// 添加字符串到长双精度浮点数的类型转换函数到 casts 数组中
casts[cast_i++] = StringToLongDoubleCastSpec;
casts[cast_i++] = LongDoubleToStringCastSpec;
// 添加字符串到复数单精度浮点数的类型转换函数到 casts 数组中
casts[cast_i++] = StringToCFloatCastSpec;
casts[cast_i++] = CFloatToStringCastSpec;
// 添加字符串到复数双精度浮点数的类型转换函数到 casts 数组中
casts[cast_i++] = StringToCDoubleCastSpec;
casts[cast_i++] = CDoubleToStringCastSpec;
// 添加字符串到复数长双精度浮点数的类型转换函数到 casts 数组中
casts[cast_i++] = StringToCLongDoubleCastSpec;
casts[cast_i++] = CLongDoubleToStringCastSpec;
// 添加字符串到无类型的类型转换函数到 casts 数组中
casts[cast_i++] = StringToVoidCastSpec;
casts[cast_i++] = VoidToStringCastSpec;
// 添加字符串到字节串的类型转换函数到 casts 数组中
casts[cast_i++] = StringToBytesCastSpec;
casts[cast_i++] = BytesToStringCastSpec;
// 将 NULL 添加到 casts 数组末尾作为结束标志
casts[cast_i++] = NULL;
// 断言最后一个元素为 NULL,确保 casts 数组以 NULL 结束
assert(casts[num_casts] == NULL);
// 断言 cast_i 的值为 num_casts + 1,确保 casts 数组中元素的数量正确
assert(cast_i == num_casts + 1);
// 返回填充完毕的 casts 数组
return casts;
}
.\numpy\numpy\_core\src\multiarray\stringdtype\casts.h
// 如果 _NPY_CORE_SRC_MULTIARRAY_STRINGDTYPE_CASTS_H_ 宏未定义,则执行以下操作
// 定义 _NPY_CORE_SRC_MULTIARRAY_STRINGDTYPE_CASTS_H_ 宏,防止重复包含
// 声明一个指向 PyArrayMethod_Spec* 类型指针数组的函数 get_casts()
PyArrayMethod_Spec **
get_casts();
// 结束条件,关闭 ifndef 宏保护区域
// 结束宏定义的注释
.\numpy\numpy\_core\src\multiarray\stringdtype\dtype.c
/* StringDType 类的实现 */
/*
* 内部辅助函数,用于创建新实例
*/
PyObject *
new_stringdtype_instance(PyObject *na_object, int coerce)
{
PyObject *new =
PyArrayDescr_Type.tp_new((PyTypeObject *)&PyArray_StringDType, NULL, NULL); // 创建新的 PyArrayDescr_Type 实例
if (new == NULL) {
return NULL; // 如果创建失败,返回空指针
}
char *default_string_buf = NULL; // 默认字符串缓冲区
char *na_name_buf = NULL; // NA 名称缓冲区
npy_string_allocator *allocator = NpyString_new_allocator(PyMem_RawMalloc, PyMem_RawFree,
PyMem_RawRealloc); // 创建字符串分配器
if (allocator == NULL) {
PyErr_SetString(PyExc_MemoryError,
"Failed to create string allocator"); // 如果分配器创建失败,设置内存错误并跳转到 fail 标签
goto fail;
}
npy_static_string default_string = {0, NULL}; // 默认静态字符串结构体
npy_static_string na_name = {0, NULL}; // NA 名称静态字符串结构体
Py_XINCREF(na_object); // 增加 NA 对象的引用计数
((PyArray_StringDTypeObject *)new)->na_object = na_object; // 设置新实例的 NA 对象指针
int has_null = na_object != NULL; // 是否具有 NULL 值
int has_nan_na = 0; // 是否具有 NaN 或 NA 值
int has_string_na = 0; // 是否具有字符串类型的 NA 值
if (has_null) {
// 如果存在缺失值标记
// 首先检查是否为字符串类型
if (PyUnicode_Check(na_object)) {
// 如果是 Python 字符串对象
has_string_na = 1;
// 获取字符串对象的 UTF-8 编码及其大小
Py_ssize_t size = 0;
const char *buf = PyUnicode_AsUTF8AndSize(na_object, &size);
if (buf == NULL) {
goto fail;
}
// 分配内存并拷贝字符串数据
default_string.buf = PyMem_RawMalloc(size);
if (default_string.buf == NULL) {
PyErr_NoMemory();
goto fail;
}
memcpy((char *)default_string.buf, buf, size);
default_string.size = size;
}
else {
// 若非字符串对象,则视为类 NaN 的对象
PyObject *ne_result = PyObject_RichCompare(na_object, na_object, Py_NE);
if (ne_result == NULL) {
goto fail;
}
// 检查比较结果是否真值
int is_truthy = PyObject_IsTrue(ne_result);
if (is_truthy == -1) {
PyErr_Clear();
has_nan_na = 1;
}
else if (is_truthy == 1) {
has_nan_na = 1;
}
Py_DECREF(ne_result);
}
// 转换缺失值对象为字符串
PyObject *na_pystr = PyObject_Str(na_object);
if (na_pystr == NULL) {
goto fail;
}
// 获取字符串对象的 UTF-8 编码及其大小
Py_ssize_t size = 0;
const char *utf8_ptr = PyUnicode_AsUTF8AndSize(na_pystr, &size);
if (utf8_ptr == NULL) {
Py_DECREF(na_pystr);
goto fail;
}
// 分配内存并拷贝字符串数据
na_name.buf = PyMem_RawMalloc(size);
if (na_name.buf == NULL) {
Py_DECREF(na_pystr);
goto fail;
}
memcpy((char *)na_name.buf, utf8_ptr, size);
na_name.size = size;
Py_DECREF(na_pystr);
}
// 转换为 PyArray_StringDTypeObject 类型的指针
PyArray_StringDTypeObject *snew = (PyArray_StringDTypeObject *)new;
// 设置结构体成员变量值
snew->has_nan_na = has_nan_na;
snew->has_string_na = has_string_na;
snew->coerce = coerce;
snew->allocator = allocator;
snew->array_owned = 0;
snew->na_name = na_name;
snew->default_string = default_string;
// 转换为 PyArray_Descr 类型的指针
PyArray_Descr *base = (PyArray_Descr *)new;
// 设置描述符的大小和对齐方式
base->elsize = SIZEOF_NPY_PACKED_STATIC_STRING;
base->alignment = ALIGNOF_NPY_PACKED_STATIC_STRING;
// 设置标志位
base->flags |= NPY_NEEDS_INIT;
base->flags |= NPY_LIST_PICKLE;
base->flags |= NPY_ITEM_REFCOUNT;
// 设置数据类型编号和类型标识
base->type_num = NPY_VSTRING;
base->kind = NPY_VSTRINGLTR;
base->type = NPY_VSTRINGLTR;
// 返回新创建的描述符对象
return new;
fail:
// 减少 new 对象的引用计数,因为返回 NULL 意味着无法返回新对象
Py_DECREF(new);
// 检查并释放默认字符串缓冲区
if (default_string_buf != NULL) {
PyMem_RawFree(default_string_buf);
}
// 检查并释放 na_name_buf 字符串缓冲区
if (na_name_buf != NULL) {
PyMem_RawFree(na_name_buf);
}
// 检查并释放分配器
if (allocator != NULL) {
NpyString_free_allocator(allocator);
}
// 返回 NULL 指示出错
return NULL;
}
static int
na_eq_cmp(PyObject *a, PyObject *b) {
// 检查对象是否完全相同,包括 None 和 Pandas.NA 这样的单例对象
if (a == b) {
return 1;
}
// 如果其中一个对象为 NULL,则返回不相等
if (a == NULL || b == NULL) {
return 0;
}
// 如果两个对象都是浮点数对象,则进行 NaN 检查
if (PyFloat_Check(a) && PyFloat_Check(b)) {
// 获取浮点数值并检查是否为 NaN
double a_float = PyFloat_AsDouble(a);
if (a_float == -1.0 && PyErr_Occurred()) {
return -1; // 出错时返回 -1
}
double b_float = PyFloat_AsDouble(b);
if (b_float == -1.0 && PyErr_Occurred()) {
return -1; // 出错时返回 -1
}
// 如果两者均为 NaN,则视为相等
if (npy_isnan(a_float) && npy_isnan(b_float)) {
return 1;
}
}
// 使用 PyObject_RichCompareBool 函数比较对象是否相等
int ret = PyObject_RichCompareBool(a, b, Py_EQ);
if (ret == -1) {
PyErr_Clear(); // 清除异常状态
return 0;
}
return ret;
}
// 设置确定 dtype 实例间相等性的逻辑规则
int
_eq_comparison(int scoerce, int ocoerce, PyObject *sna, PyObject *ona)
{
// 如果 scoerce 与 ocoerce 不相等,则返回不相等
if (scoerce != ocoerce) {
return 0;
}
// 调用 na_eq_cmp 函数比较 sna 和 ona 对象是否相等
return na_eq_cmp(sna, ona);
}
// 当处理不同 dtype 的混合时,用于确定返回的正确 dtype 实例
NPY_NO_EXPORT int
stringdtype_compatible_na(PyObject *na1, PyObject *na2, PyObject **out_na) {
// 如果 na1 和 na2 都不为空,则比较它们是否相等
if ((na1 != NULL) && (na2 != NULL)) {
int na_eq = na_eq_cmp(na1, na2);
// 如果比较出错,返回 -1
if (na_eq < 0) {
return -1;
}
// 如果 na1 和 na2 不相等,抛出类型错误异常
else if (na_eq == 0) {
PyErr_Format(PyExc_TypeError,
"Cannot find a compatible null string value for "
"null strings '%R' and '%R'", na1, na2);
return -1;
}
}
// 如果 out_na 不为空,则将 na1 或 na2 赋给 *out_na
if (out_na != NULL) {
*out_na = na1 ? na1 : na2;
}
return 0;
}
/*
* 用于确定处理不同 dtype(例如从标量列表创建数组时)时返回的正确 dtype 实例
*/
static PyArray_StringDTypeObject *
common_instance(PyArray_StringDTypeObject *dtype1, PyArray_StringDTypeObject *dtype2)
{
PyObject *out_na_object = NULL;
// 检查 na1 和 na2 是否兼容,如果不兼容,返回 NULL,并抛出类型错误异常
if (stringdtype_compatible_na(
dtype1->na_object, dtype2->na_object, &out_na_object) == -1) {
PyErr_Format(PyExc_TypeError,
"Cannot find common instance for incompatible dtypes "
"'%R' and '%R'", (PyObject *)dtype1, (PyObject *)dtype2);
return NULL;
}
// 返回新的 string dtype 实例,基于 out_na_object 和 dtype1 是否强制转换的条件
return (PyArray_StringDTypeObject *)new_stringdtype_instance(
out_na_object, dtype1->coerce && dtype1->coerce);
}
/*
* 用于确定用于数据类型提升的正确“常见”数据类型。
* cls 总是 PyArray_StringDType,other 是任意其他数据类型。
*/
static PyArray_DTypeMeta *
common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other)
{
// 如果 other 的类型码是 NPY_UNICODE
if (other->type_num == NPY_UNICODE) {
/*
* 我们需要从 unicode 进行类型转换,因此允许 unicode 转换为 PyArray_StringDType
*/
Py_INCREF(cls); // 增加 cls 的引用计数
return cls; // 返回 cls
}
Py_INCREF(Py_NotImplemented); // 增加 Py_NotImplemented 的引用计数
return (PyArray_DTypeMeta *)Py_NotImplemented; // 返回 Py_NotImplemented
}
/*
* 返回一个对 `scalar` 的字符串表示的新引用。
* 如果 scalar 不是字符串且 coerce 非零,则调用 __str__ 将其转换为字符串。
* 如果 coerce 为零,则对非字符串或非 NA 输入引发错误。
*/
static PyObject *
as_pystring(PyObject *scalar, int coerce)
{
PyTypeObject *scalar_type = Py_TYPE(scalar); // 获取 scalar 的类型对象
// 如果 scalar 的类型是 PyUnicode_Type
if (scalar_type == &PyUnicode_Type) {
Py_INCREF(scalar); // 增加 scalar 的引用计数
return scalar; // 返回 scalar
}
// 如果 coerce 为零
if (coerce == 0) {
PyErr_SetString(PyExc_ValueError,
"StringDType 只允许在禁用字符串强制转换时使用字符串数据。");
return NULL; // 返回 NULL,表示出错
}
else {
// 尝试将 scalar 转换为字符串
scalar = PyObject_Str(scalar);
if (scalar == NULL) {
// 如果 PyObject_Str 调用失败,返回 NULL
return NULL;
}
}
return scalar; // 返回 scalar
}
/*
* 从 Python 对象 `obj` 中发现描述符,并返回 PyArray_Descr 对象。
*/
static PyArray_Descr *
string_discover_descriptor_from_pyobject(PyTypeObject *NPY_UNUSED(cls),
PyObject *obj)
{
PyObject *val = as_pystring(obj, 1); // 获取 obj 的字符串表示
if (val == NULL) {
return NULL; // 如果获取失败,返回 NULL
}
Py_DECREF(val); // 释放 val 的引用计数
// 创建一个新的字符串数据类型描述符实例并返回
PyArray_Descr *ret = (PyArray_Descr *)new_stringdtype_instance(NULL, 1);
return ret; // 返回描述符实例
}
/*
* 将 Python 对象 `obj` 插入到数据类型为 `descr` 的数组中,在 dataptr 给定的位置。
*/
int
stringdtype_setitem(PyArray_StringDTypeObject *descr, PyObject *obj, char **dataptr)
{
npy_packed_static_string *sdata = (npy_packed_static_string *)dataptr;
// 借用引用
PyObject *na_object = descr->na_object;
// 在获取分配器后需要比较结果,但在获取分配器时不能使用需要 GIL 的函数,因此在获取分配器前进行比较。
// 执行 na_eq_cmp 比较
int na_cmp = na_eq_cmp(obj, na_object);
if (na_cmp == -1) {
return -1; // 如果比较失败,返回 -1
}
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 如果 na_object 不为 NULL
if (na_object != NULL) {
if (na_cmp) {
// 如果比较结果为真,尝试将空值打包到 sdata 中
if (NpyString_pack_null(allocator, sdata) < 0) {
PyErr_SetString(PyExc_MemoryError,
"Failed to pack null string during StringDType "
"setitem");
goto fail; // 打包失败,跳转到 fail 标签处处理错误
}
goto success; // 成功处理
}
}
// 将 obj 转换为字符串,并根据 descr->coerce 的值进行处理
PyObject *val_obj = as_pystring(obj, descr->coerce);
if (val_obj == NULL) {
goto fail;
}
Py_ssize_t length = 0;
const char *val = PyUnicode_AsUTF8AndSize(val_obj, &length);
if (val == NULL) {
Py_DECREF(val_obj);
goto fail;
}
if (NpyString_pack(allocator, sdata, val, length) < 0) {
PyErr_SetString(PyExc_MemoryError,
"Failed to pack string during StringDType "
"setitem");
Py_DECREF(val_obj);
goto fail;
}
Py_DECREF(val_obj);
// 释放分配给 NpyString 的内存空间
NpyString_release_allocator(allocator);
// 返回成功标志
return 0;
fail:
// 释放分配给 NpyString 的内存空间
NpyString_release_allocator(allocator);
// 返回失败标志
return -1;
}
static PyObject *
stringdtype_getitem(PyArray_StringDTypeObject *descr, char **dataptr)
{
// 初始化变量
PyObject *val_obj = NULL;
npy_packed_static_string *psdata = (npy_packed_static_string *)dataptr;
npy_static_string sdata = {0, NULL};
int has_null = descr->na_object != NULL;
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 加载字符串数据
int is_null = NpyString_load(allocator, psdata, &sdata);
// 处理加载过程中的错误
if (is_null < 0) {
PyErr_SetString(PyExc_MemoryError,
"Failed to load string in StringDType getitem");
// 转到失败处理标签
goto fail;
}
else if (is_null == 1) {
// 处理字符串为空的情况
if (has_null) {
// 返回NA对象
PyObject *na_object = descr->na_object;
Py_INCREF(na_object);
val_obj = na_object;
}
else {
// 返回空字符串对象
val_obj = PyUnicode_FromStringAndSize("", 0);
}
}
else {
// 处理字符串不为空的情况
val_obj = PyUnicode_FromStringAndSize(sdata.buf, sdata.size);
// PyPy 版本兼容性处理
val_obj = PyUnicode_FromStringAndSize(
sdata.buf == NULL ? "" : sdata.buf, sdata.size);
// 检查对象是否成功创建
if (val_obj == NULL) {
// 转到失败处理标签
goto fail;
}
}
// 释放分配给 NpyString 的内存空间
NpyString_release_allocator(allocator);
// 返回创建的对象
return val_obj;
fail:
// 处理失败情况下的资源释放
NpyString_release_allocator(allocator);
// 返回空对象
return NULL;
}
// PyArray_NonzeroFunc
// Unicode 字符串长度非零时返回真值。
npy_bool
nonzero(void *data, void *arr)
{
// 获取描述器
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)PyArray_DESCR(arr);
int has_null = descr->na_object != NULL;
int has_nan_na = descr->has_nan_na;
int has_string_na = descr->has_string_na;
// 检查空值情况
if (has_null && NpyString_isnull((npy_packed_static_string *)data)) {
// 检查字符串 NA 值的情况
if (!has_string_na) {
// 检查 NaN NA 值的情况
if (has_nan_na) {
// numpy 将 NaN 视为真值,与 Python 保持一致
return 1;
}
else {
return 0;
}
}
}
// 返回字符串长度是否非零
return NpyString_size((npy_packed_static_string *)data) != 0;
}
// PyArray_CompareFunc 的实现
// 按照字符码点比较 Unicode 字符串
int
compare(void *a, void *b, void *arr)
{
// 获取描述器
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)PyArray_DESCR(arr);
// 获取分配器并锁定互斥量
NpyString_acquire_allocator(descr);
// 执行比较操作
int ret = _compare(a, b, descr, descr);
// 释放分配给 NpyString 的内存空间
NpyString_release_allocator(descr->allocator);
// 返回比较结果
return ret;
}
// _compare 函数的实现
// 比较两个 PyArray_StringDTypeObject 对象的字符串
int
_compare(void *a, void *b, PyArray_StringDTypeObject *descr_a,
PyArray_StringDTypeObject *descr_b)
{
// 获取分配器
npy_string_allocator *allocator_a = descr_a->allocator;
npy_string_allocator *allocator_b = descr_b->allocator;
// 获取描述符 descr_b 的分配器 allocator,用于字符串分配器的操作
// descr_a and descr_b are either the same object or objects
// that are equal, so we can safely refer only to descr_a.
// This is enforced in the resolve_descriptors for comparisons
// descr_a 和 descr_b 要么是同一个对象,要么是相等的对象,因此我们可以安全地只引用 descr_a。
// 这在解析描述符用于比较时是强制执行的。
// Note that even though the default_string isn't checked in comparisons,
// it will still be the same for both descrs because the value of
// default_string is always the empty string unless na_object is a string.
// 注意,即使在比较中没有检查 default_string,它在两个描述符中仍将是相同的,
// 因为除非 na_object 是字符串,否则 default_string 的值始终为空字符串。
int has_null = descr_a->na_object != NULL;
// 检查 descr_a 是否具有空值对象 na_object
int has_string_na = descr_a->has_string_na;
// 检查 descr_a 是否有字符串的 NA 值
int has_nan_na = descr_a->has_nan_na;
// 检查 descr_a 是否有 NaN 的 NA 值
npy_static_string *default_string = &descr_a->default_string;
// 获取描述符 descr_a 的默认静态字符串指针 default_string
const npy_packed_static_string *ps_a = (npy_packed_static_string *)a;
// 将 a 转换为 npy_packed_static_string 指针类型 ps_a
npy_static_string s_a = {0, NULL};
// 初始化静态字符串 s_a,长度为 0,内容为空
int a_is_null = NpyString_load(allocator_a, ps_a, &s_a);
// 使用 allocator_a 加载 ps_a 指向的字符串数据到 s_a 中,返回是否为 null 的标志
const npy_packed_static_string *ps_b = (npy_packed_static_string *)b;
// 将 b 转换为 npy_packed_static_string 指针类型 ps_b
npy_static_string s_b = {0, NULL};
// 初始化静态字符串 s_b,长度为 0,内容为空
int b_is_null = NpyString_load(allocator_b, ps_b, &s_b);
// 使用 allocator_b 加载 ps_b 指向的字符串数据到 s_b 中,返回是否为 null 的标志
if (NPY_UNLIKELY(a_is_null == -1 || b_is_null == -1)) {
// 如果加载字符串失败(返回 -1)
char *msg = "Failed to load string in string comparison";
// 错误消息字符串
npy_gil_error(PyExc_MemoryError, msg);
// 抛出内存错误异常
return 0;
// 返回 0 表示比较失败
}
else if (NPY_UNLIKELY(a_is_null || b_is_null)) {
// 如果任一字符串为 null
if (has_null && !has_string_na) {
// 如果描述符允许 null 值且没有字符串 NA 值
if (has_nan_na) {
// 如果有 NaN 的 NA 值
if (a_is_null) {
return 1;
// a 是 null,返回 1
}
else if (b_is_null) {
return -1;
// b 是 null,返回 -1
}
}
else {
// 没有 NaN 的 NA 值,报错
npy_gil_error(
PyExc_ValueError,
"Cannot compare null that is not a nan-like value");
// 抛出值错误异常
return 0;
// 返回 0 表示比较失败
}
}
else {
// 如果描述符不允许 null 值或有字符串 NA 值
if (a_is_null) {
s_a = *default_string;
// 将 s_a 设置为默认字符串
}
if (b_is_null) {
s_b = *default_string;
// 将 s_b 设置为默认字符串
}
}
}
// 返回字符串 s_a 和 s_b 的比较结果
return NpyString_cmp(&s_a, &s_b);
}
// PyArray_ArgFunc
// 返回数组中具有最高Unicode代码点的元素的索引。
int
argmax(char *data, npy_intp n, npy_intp *max_ind, void *arr)
{
// 获取数组描述符
PyArray_Descr *descr = PyArray_DESCR(arr);
// 获取元素大小
npy_intp elsize = descr->elsize;
// 初始化最大索引为0
*max_ind = 0;
// 遍历数组
for (int i = 1; i < n; i++) {
// 比较当前元素与当前最大元素的Unicode代码点
if (compare(data + i * elsize, data + (*max_ind) * elsize, arr) > 0) {
// 更新最大元素的索引
*max_ind = i;
}
}
return 0;
}
// PyArray_ArgFunc
// 返回数组中具有最低Unicode代码点的元素的索引。
int
argmin(char *data, npy_intp n, npy_intp *min_ind, void *arr)
{
// 获取数组描述符
PyArray_Descr *descr = PyArray_DESCR(arr);
// 获取元素大小
npy_intp elsize = descr->elsize;
// 初始化最小索引为0
*min_ind = 0;
// 遍历数组
for (int i = 1; i < n; i++) {
// 比较当前元素与当前最小元素的Unicode代码点
if (compare(data + i * elsize, data + (*min_ind) * elsize, arr) < 0) {
// 更新最小元素的索引
*min_ind = i;
}
}
return 0;
}
static PyArray_StringDTypeObject *
stringdtype_ensure_canonical(PyArray_StringDTypeObject *self)
{
// 增加引用计数,确保字符串数据类型对象规范化
Py_INCREF(self);
return self;
}
static int
stringdtype_clear_loop(void *NPY_UNUSED(traverse_context),
const PyArray_Descr *descr, char *data, npy_intp size,
npy_intp stride, NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取字符串数据类型对象
PyArray_StringDTypeObject *sdescr = (PyArray_StringDTypeObject *)descr;
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(sdescr);
// 遍历数据,释放每个字符串
while (size--) {
npy_packed_static_string *sdata = (npy_packed_static_string *)data;
// 如果数据不为空,释放字符串内存
if (data != NULL && NpyString_free(sdata, allocator) < 0) {
// 在清理循环中发生内存错误时,抛出异常
npy_gil_error(PyExc_MemoryError,
"String deallocation failed in clear loop");
goto fail;
}
// 移动到下一个字符串
data += stride;
}
// 释放字符串分配器
NpyString_release_allocator(allocator);
return 0;
fail:
// 失败时释放字符串分配器
NpyString_release_allocator(allocator);
return -1;
}
static int
stringdtype_get_clear_loop(void *NPY_UNUSED(traverse_context),
PyArray_Descr *NPY_UNUSED(descr),
int NPY_UNUSED(aligned),
npy_intp NPY_UNUSED(fixed_stride),
PyArrayMethod_TraverseLoop **out_loop,
NpyAuxData **NPY_UNUSED(out_auxdata),
NPY_ARRAYMETHOD_FLAGS *flags)
{
// 设置标志以避免浮点错误
*flags = NPY_METH_NO_FLOATINGPOINT_ERRORS;
// 设置清理循环函数指针
*out_loop = &stringdtype_clear_loop;
return 0;
}
static int
stringdtype_is_known_scalar_type(PyArray_DTypeMeta *cls,
PyTypeObject *pytype)
{
// 检查Python内建类型是否为已知的标量类型
if (python_builtins_are_known_scalar_types(cls, pytype)) {
return 1;
}
// 接受所有内建的numpy数据类型
return 1;
}
// 如果 pytype 是以下任意一种数组类型,则返回 1
else if (pytype == &PyBoolArrType_Type ||
pytype == &PyByteArrType_Type ||
pytype == &PyShortArrType_Type ||
pytype == &PyIntArrType_Type ||
pytype == &PyLongArrType_Type ||
pytype == &PyLongLongArrType_Type ||
pytype == &PyUByteArrType_Type ||
pytype == &PyUShortArrType_Type ||
pytype == &PyUIntArrType_Type ||
pytype == &PyULongArrType_Type ||
pytype == &PyULongLongArrType_Type ||
pytype == &PyHalfArrType_Type ||
pytype == &PyFloatArrType_Type ||
pytype == &PyDoubleArrType_Type ||
pytype == &PyLongDoubleArrType_Type ||
pytype == &PyCFloatArrType_Type ||
pytype == &PyCDoubleArrType_Type ||
pytype == &PyCLongDoubleArrType_Type ||
pytype == &PyIntpArrType_Type ||
pytype == &PyUIntpArrType_Type ||
pytype == &PyDatetimeArrType_Type ||
pytype == &PyTimedeltaArrType_Type)
{
// 返回 1 表示 pytype 是数组类型之一
return 1;
}
// 否则返回 0
return 0;
}
// 结束函数 stringdtype_finalize_descr
PyArray_Descr *
stringdtype_finalize_descr(PyArray_Descr *dtype)
{
// 将传入的 dtype 转换为 PyArray_StringDTypeObject 类型
PyArray_StringDTypeObject *sdtype = (PyArray_StringDTypeObject *)dtype;
// 如果 sdtype 指向的数组未被所有者拥有
if (sdtype->array_owned == 0) {
// 标记数组已被所有者拥有
sdtype->array_owned = 1;
// 增加 dtype 的引用计数
Py_INCREF(dtype);
// 返回原始的 dtype
return dtype;
}
// 否则创建一个新的 PyArray_StringDTypeObject 实例,使用 sdtype 的 na_object 和 coerce 属性
PyArray_StringDTypeObject *ret = (PyArray_StringDTypeObject *)new_stringdtype_instance(
sdtype->na_object, sdtype->coerce);
// 标记新实例的数组已被所有者拥有
ret->array_owned = 1;
// 返回新实例的描述符
return (PyArray_Descr *)ret;
}
// 静态数组 PyArray_StringDType_Slots 的定义和初始化
static PyType_Slot PyArray_StringDType_Slots[] = {
{NPY_DT_common_instance, &common_instance},
{NPY_DT_common_dtype, &common_dtype},
{NPY_DT_discover_descr_from_pyobject,
&string_discover_descriptor_from_pyobject},
{NPY_DT_setitem, &stringdtype_setitem},
{NPY_DT_getitem, &stringdtype_getitem},
{NPY_DT_ensure_canonical, &stringdtype_ensure_canonical},
{NPY_DT_PyArray_ArrFuncs_nonzero, &nonzero},
{NPY_DT_PyArray_ArrFuncs_compare, &compare},
{NPY_DT_PyArray_ArrFuncs_argmax, &argmax},
{NPY_DT_PyArray_ArrFuncs_argmin, &argmin},
{NPY_DT_get_clear_loop, &stringdtype_get_clear_loop},
{NPY_DT_finalize_descr, &stringdtype_finalize_descr},
{_NPY_DT_is_known_scalar_type, &stringdtype_is_known_scalar_type},
{0, NULL}};
// 创建新的 StringDType 实例
static PyObject *
stringdtype_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwds)
{
// 定义关键字参数数组
static char *kwargs_strs[] = {"coerce", "na_object", NULL};
PyObject *na_object = NULL;
int coerce = 1;
// 使用 PyArg_ParseTupleAndKeywords 解析参数
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$pO&:StringDType",
kwargs_strs, &coerce,
_not_NoValue, &na_object)) {
return NULL;
}
// 返回创建的新 StringDType 实例
return new_stringdtype_instance(na_object, coerce);
}
// 释放 StringDType 对象的内存
static void
stringdtype_dealloc(PyArray_StringDTypeObject *self)
{
// 释放 na_object 的引用
Py_XDECREF(self->na_object);
// 如果 allocator 不为空,则释放其分配的资源
if (self->allocator != NULL) {
NpyString_free_allocator(self->allocator);
}
// 释放 na_name 和 default_string 的内存
PyMem_RawFree((char *)self->na_name.buf);
PyMem_RawFree((char *)self->default_string.buf);
// 调用父类的 dealloc 方法释放对象内存
PyArrayDescr_Type.tp_dealloc((PyObject *)self);
}
// 返回 StringDType 对象的字符串表示形式
static PyObject *
stringdtype_repr(PyArray_StringDTypeObject *self)
{
PyObject *ret = NULL;
// 借用 na_object 的引用
PyObject *na_object = self->na_object;
int coerce = self->coerce;
// 根据 na_object 和 coerce 属性生成不同的字符串表示形式
if (na_object != NULL && coerce == 0) {
ret = PyUnicode_FromFormat("StringDType(na_object=%R, coerce=False)",
na_object);
}
else if (na_object != NULL) {
ret = PyUnicode_FromFormat("StringDType(na_object=%R)", na_object);
}
else if (coerce == 0) {
ret = PyUnicode_FromFormat("StringDType(coerce=False)", coerce);
}
else {
ret = PyUnicode_FromString("StringDType()");
}
return ret;
}
// 实现 __reduce__ 魔法方法以重建 StringDType 对象
// 导入 "numpy._core._internal" 模块中的 "_convert_to_stringdtype_kwargs" 函数,
// 并将其存储在全局变量 npy_thread_unsafe_state._convert_to_stringdtype_kwargs 中。
// 这个操作并不是性能关键,仅仅是为了方便使用 Python 中的 pickle 模块进行对象序列化。
static PyObject *
stringdtype__reduce__(PyArray_StringDTypeObject *self, PyObject *NPY_UNUSED(args))
{
npy_cache_import("numpy._core._internal", "_convert_to_stringdtype_kwargs",
&npy_thread_unsafe_state._convert_to_stringdtype_kwargs);
// 如果未能成功导入 _convert_to_stringdtype_kwargs 函数,返回 NULL 表示错误。
if (npy_thread_unsafe_state._convert_to_stringdtype_kwargs == NULL) {
return NULL;
}
// 如果 self->na_object 不为 NULL,则返回一个包含三个元素的元组:
// (npy_thread_unsafe_state._convert_to_stringdtype_kwargs, self->coerce, self->na_object)。
if (self->na_object != NULL) {
return Py_BuildValue(
"O(iO)", npy_thread_unsafe_state._convert_to_stringdtype_kwargs,
self->coerce, self->na_object);
}
// 如果 self->na_object 为 NULL,则返回一个包含两个元素的元组:
// (npy_thread_unsafe_state._convert_to_stringdtype_kwargs, self->coerce)。
return Py_BuildValue(
"O(i)", npy_thread_unsafe_state._convert_to_stringdtype_kwargs,
self->coerce);
}
// 定义 PyArray_StringDType 对象的方法列表
static PyMethodDef PyArray_StringDType_methods[] = {
{
"__reduce__", // 方法名
(PyCFunction)stringdtype__reduce__, // 方法实现函数
METH_NOARGS, // 方法接受的参数类型标志
"Reduction method for a StringDType object", // 方法的文档字符串
},
{NULL, NULL, 0, NULL}, // 方法列表结束标志
};
// 定义 PyArray_StringDType 对象的成员变量列表
static PyMemberDef PyArray_StringDType_members[] = {
{"na_object", // 成员变量名
T_OBJECT_EX, // 变量的数据类型
offsetof(PyArray_StringDTypeObject, na_object), // 变量在结构体中的偏移量
READONLY, // 变量的标志,表示只读
"The missing value object associated with the dtype instance"}, // 变量的文档字符串
{"coerce", // 成员变量名
T_BOOL, // 变量的数据类型
offsetof(PyArray_StringDTypeObject, coerce), // 变量在结构体中的偏移量
READONLY, // 变量的标志,表示只读
"Controls whether non-string values should be coerced to string"}, // 变量的文档字符串
{NULL, 0, 0, 0, NULL}, // 成员变量列表结束标志
};
// 定义 PyArray_StringDType 对象的富比较方法
static PyObject *
PyArray_StringDType_richcompare(PyObject *self, PyObject *other, int op)
{
// 如果 op 不是 Py_EQ 或 Py_NE,或者 other 不是 self 的同一类型,返回 NotImplemented。
if (((op != Py_EQ) && (op != Py_NE)) ||
(Py_TYPE(other) != Py_TYPE(self))) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
// 将 self 和 other 转换为 PyArray_StringDTypeObject 类型,因为我们已知它们是该类型的实例。
PyArray_StringDTypeObject *sself = (PyArray_StringDTypeObject *)self;
PyArray_StringDTypeObject *sother = (PyArray_StringDTypeObject *)other;
// 调用 _eq_comparison 函数比较 self 和 other 的 coerce 和 na_object 成员变量。
int eq = _eq_comparison(sself->coerce, sother->coerce, sself->na_object,
sother->na_object);
// 如果 _eq_comparison 返回 -1,表示比较出错,返回 NULL。
if (eq == -1) {
return NULL;
}
// 根据比较结果和 op 的值返回 Py_True 或 Py_False。
if ((op == Py_EQ && eq) || (op == Py_NE && !eq)) {
Py_INCREF(Py_True);
return Py_True;
}
Py_INCREF(Py_False);
return Py_False;
}
// 定义 PyArray_StringDType 对象的哈希计算方法
static Py_hash_t
PyArray_StringDType_hash(PyObject *self)
{
// 将 self 转换为 PyArray_StringDTypeObject 类型。
PyArray_StringDTypeObject *sself = (PyArray_StringDTypeObject *)self;
PyObject *hash_tup = NULL;
// 如果 sself->na_object 不为 NULL,则构建一个包含两个元素的元组 (sself->coerce, sself->na_object)。
// 否则,构建一个包含一个元素的元组 (sself->coerce)。
if (sself->na_object != NULL) {
hash_tup = Py_BuildValue("(iO)", sself->coerce, sself->na_object);
}
else {
hash_tup = Py_BuildValue("(i)", sself->coerce);
}
// 计算元组 hash_tup 的哈希值,并返回结果。
Py_hash_t ret = PyObject_Hash(hash_tup);
Py_DECREF(hash_tup);
return ret;
}
"""
/*
* This is the basic things that you need to create a Python Type/Class in C.
* However, there is a slight difference here because we create a
* PyArray_DTypeMeta, which is a larger struct than a typical type.
* (This should get a bit nicer eventually with Python >3.11.)
*/
*/
PyArray_DTypeMeta PyArray_StringDType = {
{{
// 设置类型名称为 "numpy.dtypes.StringDType",基本大小为 PyArray_StringDTypeObject 的大小
PyVarObject_HEAD_INIT(NULL, 0).tp_name =
"numpy.dtypes.StringDType",
// 分配内存空间大小为 PyArray_StringDTypeObject 的大小
.tp_basicsize = sizeof(PyArray_StringDTypeObject),
// 设置构造函数为 stringdtype_new
.tp_new = stringdtype_new,
// 设置析构函数为 stringdtype_dealloc
.tp_dealloc = (destructor)stringdtype_dealloc,
// 设置对象的字符串表示形式为 stringdtype_repr
.tp_repr = (reprfunc)stringdtype_repr,
// 设置对象的字符串形式为 stringdtype_repr
.tp_str = (reprfunc)stringdtype_repr,
// 设置对象的方法为 PyArray_StringDType_methods
.tp_methods = PyArray_StringDType_methods,
// 设置对象的成员为 PyArray_StringDType_members
.tp_members = PyArray_StringDType_members,
// 设置对象的比较函数为 PyArray_StringDType_richcompare
.tp_richcompare = PyArray_StringDType_richcompare,
// 设置对象的哈希函数为 PyArray_StringDType_hash
.tp_hash = PyArray_StringDType_hash,
}},
/* rest, filled in during DTypeMeta initialization */
};
NPY_NO_EXPORT int
init_string_dtype(void)
{
// 获取字符串类型的强制转换列表
PyArrayMethod_Spec **PyArray_StringDType_casts = get_casts();
// 字符串类型的元数据规范
PyArrayDTypeMeta_Spec PyArray_StringDType_DTypeSpec = {
// 标志为 NPY_DT_PARAMETRIC
.flags = NPY_DT_PARAMETRIC,
// 类型对象为 PyUnicode_Type
.typeobj = &PyUnicode_Type,
// 槽位为 PyArray_StringDType_Slots
.slots = PyArray_StringDType_Slots,
// 强制转换为 PyArray_StringDType_casts
.casts = PyArray_StringDType_casts,
};
/* Loaded dynamically, so needs to be set here: */
// 将 PyArray_StringDType 的类型设置为 PyArrayDTypeMeta_Type
((PyObject *)&PyArray_StringDType)->ob_type = &PyArrayDTypeMeta_Type;
// 将 PyArray_StringDType 的基类设置为 PyArrayDescr_Type
((PyTypeObject *)&PyArray_StringDType)->tp_base = &PyArrayDescr_Type;
// 如果 PyType_Ready((PyTypeObject *)&PyArray_StringDType) 小于 0,则返回 -1
if (PyType_Ready((PyTypeObject *)&PyArray_StringDType) < 0) {
return -1;
}
// 如果 dtypemeta_initialize_struct_from_spec 初始化失败,则返回 -1
if (dtypemeta_initialize_struct_from_spec(
&PyArray_StringDType, &PyArray_StringDType_DTypeSpec, 1) < 0) {
return -1;
}
// 获取默认的描述符
PyArray_Descr *singleton =
NPY_DT_CALL_default_descr(&PyArray_StringDType);
// 如果 singleton 为空,则返回 -1
if (singleton == NULL) {
return -1;
}
// 设置 PyArray_StringDType 的单例为 singleton
PyArray_StringDType.singleton = singleton;
// 设置 PyArray_StringDType 的类型编号为 NPY_VSTRING
PyArray_StringDType.type_num = NPY_VSTRING;
// 释放 PyArray_StringDType_casts 的内存
for (int i = 0; PyArray_StringDType_casts[i] != NULL; i++) {
PyMem_Free(PyArray_StringDType_casts[i]->dtypes);
PyMem_Free(PyArray_StringDType_casts[i]);
}
PyMem_Free(PyArray_StringDType_casts);
// 返回成功
return 0;
}
int
free_and_copy(npy_string_allocator *in_allocator,
npy_string_allocator *out_allocator,
const npy_packed_static_string *in,
npy_packed_static_string *out, const char *location)
{
// 释放输出的字符串内存
if (NpyString_free(out, out_allocator) < 0) {
// 如果释放失败,则报错并返回 -1
npy_gil_error(PyExc_MemoryError, "Failed to deallocate string in %s", location);
return -1;
}
// 复制输入到输出字符串
if (NpyString_dup(in, out, in_allocator, out_allocator) < 0) {
// 如果复制失败,则报错并返回 -1
npy_gil_error(PyExc_MemoryError, "Failed to allocate string in %s", location);
return -1;
}
// 操作成功,返回 0
return 0;
}
/*
* 使用一个预定义的 npy_static_string 实例,分配到栈上并初始化为 {0, NULL},
* 将指向这个栈上分配的未打包字符串的指针传递给该函数,用来填充新分配字符串的内容。
*/
NPY_NO_EXPORT int
load_new_string(npy_packed_static_string *out, npy_static_string *out_ss,
size_t num_bytes, npy_string_allocator *allocator,
const char *err_context)
{
// 将输出参数 out 强制转换为 npy_packed_static_string 指针
npy_packed_static_string *out_pss = (npy_packed_static_string *)out;
// 如果调用 NpyString_free 函数释放 out_pss 失败
if (NpyString_free(out_pss, allocator) < 0) {
// 报告内存错误,并指明错误上下文
npy_gil_error(PyExc_MemoryError,
"Failed to deallocate string in %s", err_context);
return -1; // 返回错误码
}
// 调用 NpyString_newemptysize 函数尝试分配 num_bytes 大小的新字符串到 out_pss
if (NpyString_newemptysize(num_bytes, out_pss, allocator) < 0) {
// 报告内存错误,并指明错误上下文
npy_gil_error(PyExc_MemoryError,
"Failed to allocate string in %s", err_context);
return -1; // 返回错误码
}
// 调用 NpyString_load 函数加载字符串到 out_ss,并使用 allocator 进行分配
if (NpyString_load(allocator, out_pss, out_ss) == -1) {
// 报告内存错误,并指明错误上下文
npy_gil_error(PyExc_MemoryError,
"Failed to load string in %s", err_context);
return -1; // 返回错误码
}
// 操作成功完成,返回成功码
return 0;
}