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

基于SWIG实现Python模块:C函数eval的列表输入输出适配

用SWIG封装C eval函数:搞定Python输入输出适配

我之前做数值计算模块的时候刚好遇到过几乎一模一样的需求,给你一步步拆解怎么把C的void eval(double *x, int nx, int mx, double *f, int func_id)封装成Python里好用的value_list = module.eval(point_matrix, func_id)接口:

先明确参数对应关系

不用记那些C里的指针参数,先把Python和C的参数对应搞明白:

  • Python的point_matrix:就是你要计算的mx个nx维点,对应C里的x数组,同时我们可以从它里面自动算出nx(每个点的维度)和mx(点的数量),不用用户手动传这俩参数
  • Python的func_id:直接传给C的同名参数,用来指定要调用的基准函数
  • C里的f数组:存储每个点的计算结果,我们要把它转换成Python列表value_list返回给用户

编写SWIG接口文件(核心是typemap)

SWIG的typemap是做这种参数适配的神器,不用手动写胶水代码,直接在接口文件里定义转换规则就行。比如新建一个func_eval.i文件:

%module func_eval

%{
#include "your_c_funcs.h"  // 替换成你的C函数头文件路径
#include <stdlib.h>
%}

// --------------------------
// 处理输入:把Python的点矩阵转成C的double数组
// --------------------------
%typemap(in) (double *x, int nx, int mx) {
    // 先检查输入是不是合法的二维序列(列表/元组都行)
    if (!PySequence_Check($input)) {
        PyErr_SetString(PyExc_TypeError, "参数point_matrix必须是二维序列,比如[[1,2],[3,4]]");
        return NULL;
    }
    // 拿到点的数量mx
    $3 = PySequence_Length($input);
    if ($3 <= 0) {
        PyErr_SetString(PyExc_ValueError, "point_matrix里至少要有一个点");
        return NULL;
    }
    // 拿到第一个点的维度nx,顺便检查第一个点是不是序列
    PyObject *first_point = PySequence_GetItem($input, 0);
    if (!PySequence_Check(first_point)) {
        PyErr_SetString(PyExc_TypeError, "point_matrix里的每个元素必须是一维序列(单个点)");
        Py_DECREF(first_point);
        return NULL;
    }
    $2 = PySequence_Length(first_point);
    Py_DECREF(first_point);
    if ($2 <= 0) {
        PyErr_SetString(PyExc_ValueError, "每个点至少要有一个维度");
        return NULL;
    }
    // 给C的x数组分配内存
    $1 = (double*)malloc($2 * $3 * sizeof(double));
    if (!$1) {
        PyErr_SetString(PyExc_MemoryError, "内存分配失败,无法存储点数据");
        return NULL;
    }
    // 把Python里的点数据复制到C数组里
    for (int i = 0; i < $3; i++) {
        PyObject *point = PySequence_GetItem($input, i);
        // 检查每个点的维度是不是和第一个点一致
        if (!PySequence_Check(point) || PySequence_Length(point) != $2) {
            PyErr_SetString(PyExc_ValueError, "所有点的维度必须相同");
            free($1);
            Py_DECREF(point);
            return NULL;
        }
        // 复制每个维度的值
        for (int j = 0; j < $2; j++) {
            PyObject *val = PySequence_GetItem(point, j);
            $1[i * $2 + j] = PyFloat_AsDouble(val);
            Py_DECREF(val);
        }
        Py_DECREF(point);
    }
}

// --------------------------
// 处理输出:给f数组分配内存,调用完C函数后转成Python列表
// --------------------------
// 先给f分配内存(numinputs=0表示这个参数不需要用户传,我们自动处理)
%typemap(in, numinputs=0) double *f (double *temp_f) {
    $1 = temp_f = (double*)malloc($3 * sizeof(double));  // $3是之前拿到的mx,点的数量
    if (!$1) {
        PyErr_SetString(PyExc_MemoryError, "内存分配失败,无法存储计算结果");
        return NULL;
    }
}

// 调用完C函数后,把f数组转成Python列表返回
%typemap(argout) double *f {
    PyObject *result_list = PyList_New($3);
    for (int i = 0; i < $3; i++) {
        PyList_SetItem(result_list, i, PyFloat_FromDouble($1[i]));
    }
    // 把结果加到SWIG的返回值里
    $result = SWIG_Python_AppendOutput($result, result_list);
}

// 最后释放f的内存,避免泄漏
%typemap(freearg) double *f {
    free($1);
}

// --------------------------
// 声明要封装的C函数,SWIG会自动应用上面的typemap
// --------------------------
void eval(double *x, int nx, int mx, double *f, int func_id);

编译和测试步骤

假设你的C代码文件是func_eval.c,按下面的步骤编译成Python模块:

  1. 用SWIG生成胶水代码:
    swig -python func_eval.i
    
  2. 编译C代码和胶水代码:
    # 替换成你的Python头文件路径,比如/usr/include/python3.8
    gcc -fPIC -c func_eval.c func_eval_wrap.c -I/usr/include/python3.x
    
  3. 生成共享库:
    gcc -shared func_eval.o func_eval_wrap.o -o _func_eval.so
    

然后就可以在Python里调用了:

import func_eval

# 测试:2个3维点,调用func_id=1的函数
points = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
results = func_eval.eval(points, 1)
print(results)  # 输出每个点的计算结果列表

可选优化:支持numpy数组

如果你的项目里常用numpy,可以添加SWIG的numpy支持,不用复制数据,直接用numpy数组的内存指针,效率更高:

  • 在接口文件开头加入%include "numpy.i"和%init %{import_array();%}
  • 用numpy的typemap替换原来的输入处理typemap,具体可以查SWIG的numpy文档,这里就不展开了

这样封装出来的Python接口完全符合你的需求,用户不用关心底层的C参数细节,用起来和普通Python函数一样顺手。

内容的提问来源于stack exchange,提问作者逸峰李

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:33:31