NumPy C-API循环中NPY_DATETIME元单元被覆盖问题及解决
问题:NumPy datetime64/timedelta64 元数据被覆盖导致单位统一错误
我尝试通过PyArray_DESCR为Date类型设置NPY_FR_D单位,为DateTime和Time类型设置NPY_FR_s单位,将处理后的数组加入boost::python::list返回Python,但循环中metadata->meta.base的值会被覆盖,最终返回的列表里所有类型都变成最后一次设置的单位,要么全是datetime64[s]要么全是datetime64[D]。根据NumPy C-API文档,PyArray_DESCR返回的是数组dtype属性的借用引用。
以下是问题代码:
for (unsigned c = 0; c < numFields; ++c) { int typeNum; switch (m_typeVector[c]) { case SRC::E_FieldType::E_FT_Date: case SRC::E_FieldType::E_FT_DateTime: { typeNum = NPY_DATETIME; break; } case SRC::E_FieldType::E_FT_Time: { typeNum = NPY_TIMEDELTA; break; } default: typeNum = NPY_NOTYPE; break; } SRC::Python::NumpyTransfer* colData = new SRC::Python::NumpyTransfer; if (typeNum != NPY_NOTYPE) { PyObject* npArr = PyArray_SimpleNew(1, dims, typeNum); PyArray_ENABLEFLAGS((PyArrayObject*)npArr, NPY_ARRAY_OWNDATA); auto descr = PyArray_DESCR(reinterpret_cast<PyArrayObject*>(npArr)); auto metadata = reinterpret_cast<PyArray_DatetimeDTypeMetaData*>(descr->c_metadata); if (typeNum == NPY_DATETIME && m_typeVector[c] == SRC::E_FieldType::E_FT_DateTime) { metadata->meta.base = NPY_FR_s; } else if (typeNum == NPY_DATETIME && m_typeVector[c] == SRC::E_FieldType::E_FT_Date) { metadata->meta.base = NPY_FR_D; } else if (typeNum == NPY_TIMEDELTA && m_typeVector[c] == SRC::E_FieldType::E_FT_Time) { metadata->meta.base = NPY_FR_s; } colData->m_numpyArray = boost::python::handle<>(npArr); rawData.push_back(PyArray_DATA((PyArrayObject*)npArr)); colData->m_isNumpy = true; // ... 其他逻辑 colStorage.push_back(colData); } }
解决方案:创建独立的dtype描述符副本
问题根源:直接修改PyArray_SimpleNew返回数组的dtype描述符时,所有同基础类型(如NPY_DATETIME)的数组共享同一个全局描述符实例,后续修改会覆盖之前的设置。
正确实现步骤:
- 用
PyArray_DescrNewFromType创建PyArray_Descr结构的独立副本 - 修改该副本的元数据(单位信息)
- 通过
PyArray_SimpleNewFromDescr使用自定义描述符创建数组,确保每个数组拥有独立的dtype描述符
修改后的关键代码:
for (unsigned c = 0; c < numFields; ++c) { int typeNum; switch (m_typeVector[c]) { case SRC::E_FieldType::E_FT_Date: case SRC::E_FieldType::E_FT_DateTime: typeNum = NPY_DATETIME; break; case SRC::E_FieldType::E_FT_Time: typeNum = NPY_TIMEDELTA; break; default: typeNum = NPY_NOTYPE; break; } SRC::Python::NumpyTransfer* colData = new SRC::Python::NumpyTransfer; if (typeNum != NPY_NOTYPE) { // 创建独立的dtype描述符副本 PyObject* descr = PyArray_DescrNewFromType(typeNum); if (!descr) { // 补充错误处理逻辑 continue; } // 修改描述符的元数据 auto dtype = reinterpret_cast<PyArray_Descr*>(descr); auto metadata = reinterpret_cast<PyArray_DatetimeDTypeMetaData*>(dtype->c_metadata); if (typeNum == NPY_DATETIME && m_typeVector[c] == SRC::E_FieldType::E_FT_DateTime) { metadata->meta.base = NPY_FR_s; } else if (typeNum == NPY_DATETIME && m_typeVector[c] == SRC::E_FieldType::E_FT_Date) { metadata->meta.base = NPY_FR_D; } else if (typeNum == NPY_TIMEDELTA && m_typeVector[c] == SRC::E_FieldType::E_FT_Time) { metadata->meta.base = NPY_FR_s; } // 使用自定义描述符创建数组 PyObject* npArr = PyArray_SimpleNewFromDescr(1, dims, dtype); Py_DECREF(descr); // 释放描述符引用,数组会持有自身的引用 PyArray_ENABLEFLAGS((PyArrayObject*)npArr, NPY_ARRAY_OWNDATA); colData->m_numpyArray = boost::python::handle<>(npArr); rawData.push_back(PyArray_DATA((PyArrayObject*)npArr)); colData->m_isNumpy = true; // ... 其他逻辑 colStorage.push_back(colData); } }
内容的提问来源于stack exchange,提问作者Ketan Chauhan
相关产品推荐
相关产品推荐

