You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python C扩展问题:无法将浮点值存入数组(类型不匹配错误)

问题:Python C扩展中PyFloat_AsDouble调用报错,PyLong_AsLong却正常

正在学习C语言,尝试编写Python C扩展实现COO稀疏数据到稠密数组的转换(已知SciPy可实现该功能),但遇到阻碍。能通过PyLong_AsLong获取整数列表中的值,却无法用PyFloat_AsDouble获取浮点列表中的值,不清楚两者的差异导致报错的原因。

报错信息

motion_tools.c:20:68: error: incompatible type for argument 1 of 'PyFloat_AsDouble'
   20 |                 double v = (double) PyFloat_AsDouble(val_float_list[i]);
      |                                                      ~~~~~~~~~~~~~~^~~
      |                                                                    |
      |                                                                    PyObject {aka struct _object}
In file included from /usr/include/python3.10/Python.h:87,
                 from motion_tools.c:2:
/usr/include/python3.10/floatobject.h:49:37: note: expected 'PyObject *' {aka 'struct _object *'} but argument is of type 'PyObject' {aka 'struct _object'}
   49 | PyAPI_FUNC(double) PyFloat_AsDouble(PyObject *);
      |                                     ^~~~~~~~~~

测试代码(tool_test.py)

# tool_test.py
import motion_tools

motion_tools.sparse_to_dense([0,1],[0,1],[1.0,1.0],(2,2))

C扩展代码(motion_tools.c)

// motion_tools.c
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h>

PyObject *sparse2dense(PyObject* self, PyObject* args) {
    PyObject* x_int_list; // 传入的Python列表
    PyObject* y_int_list; // 传入的Python列表
    PyObject* val_float_list; // 传入的Python列表
    PyObject* shape_int_tuple; // 传入的Python元组

    if (!PyArg_ParseTuple(args, "OOOO", &x_int_list, &y_int_list, &val_float_list, &shape_int_tuple))
        return NULL;

    PyObject *out_array = PyArray_Zeros(2, shape_int_tuple);
    Py_ssize_t element_count = PyList_Size(x_int_list);
    int i;
    for (i=0; i<element_count; ++i){
        int x = (int) PyLong_AsLong(x_int_list[i]);  // 此代码可正常运行
        int y = (int) PyLong_AsLong(y_int_list[i]);  // 此代码可正常运行
        double v = (double) PyFloat_AsDouble(val_float_list[i]);  // 此代码运行报错

        //out_array[y][x] = v;
        PyArray_SETITEM(out_array, PyArray_GETPTR2(out_array, y, x), v);

    }

    return PyArray_Return(out_array);
}

static PyModuleDef methods[] = {
    { "sparse_to_dense", sparse2dense, METH_VARARGS, "将COO格式转换为稠密数组"},
    { NULL, NULL, 0, NULL }
};

static struct PyModuleDef sparse_to_dense = {
    PyModuleDef_HEAD_INIT,
    "motion_tools",
    "稀疏转稠密模块",
    -1,
    methods
};

PyMODINIT_FUNC PyInit_motion_tools(void) {
    return PyModule_Create(&sparse_to_dense);
}

问题原因与修复

核心原因

报错本质是错误地用C数组下标语法访问Python列表对象:
x_int_list[i]、val_float_list[i]这种写法在C中是访问数组元素,但x_int_list是PyObject*(指向Python列表的指针),不是C数组。这种写法会直接解引用指针偏移后的内存,得到的是PyObject类型(结构体实例),而PyLong_AsLong和PyFloat_AsDouble都要求参数是PyObject*(结构体指针),因此类型不匹配导致报错。之前PyLong_AsLong没报错可能是编译器的巧合,实际上同样存在问题。

修复方案

必须使用Python C API提供的PyList_GetItem函数获取列表元素,它会返回指向列表元素的PyObject*,符合函数参数要求。同时注意PyArray_SETITEM的参数要求,需要将C的double转为Python的float对象。

修复后的循环代码:

for (i=0; i<element_count; ++i){
    // 获取列表中的元素指针
    PyObject* x_item = PyList_GetItem(x_int_list, i);
    int x = (int) PyLong_AsLong(x_item);

    PyObject* y_item = PyList_GetItem(y_int_list, i);
    int y = (int) PyLong_AsLong(y_item);

    PyObject* val_item = PyList_GetItem(val_float_list, i);
    double v = PyFloat_AsDouble(val_item);  // 返回值本身就是double,无需强制转换

    // 将C的double转为Python float对象,再设置到数组中
    PyArray_SETITEM(out_array, PyArray_GETPTR2(out_array, y, x), PyFloat_FromDouble(v));
}

额外注意事项

  1. PyList_GetItem返回的是借用引用,不需要手动调用Py_DECREF释放。
  2. 建议添加错误检查:比如判断PyList_GetItem是否返回NULL,以及PyLong_AsLong/PyFloat_AsDouble转换是否失败(可通过PyErr_Occurred()判断),避免程序崩溃。
  3. PyArray_Zeros需要确保shape_int_tuple是合法的整数元组,否则会返回NULL,建议提前检查。

内容的提问来源于stack exchange,提问作者user1209675

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 13:42:03