如何实现Python代码与C代码之间的互通调用
Python与C代码互通调用方案
场景1:Python调用C侧定义的函数
以下是3种常用实现方式,按上手难度从低到高排序:
- ctypes方案(最易用,无需修改原有C代码)
原理是直接加载C编译的动态库,在Python侧做类型映射后直接调用函数。- 编写C代码(以
test.c为例):
// 示例C函数 int add(int a, int b) { return a + b; }- 编译为动态库:
- Linux/macOS 执行命令:
gcc -shared -fPIC test.c -o libtest.so - Windows 执行命令:
gcc -shared test.c -o test.dll
- Linux/macOS 执行命令:
- Python侧调用代码:
import ctypes # 加载动态库,Windows替换路径为test.dll lib = ctypes.CDLL("./libtest.so") # 可选:显式指定参数、返回值类型,避免类型不匹配错误 lib.add.argtypes = [ctypes.c_int, ctypes.c_int] lib.add.restype = ctypes.c_int # 调用C函数 res = lib.add(1, 2) print(res) # 输出3 - 编写C代码(以
- Python C API 方案(性能最高,适合写高性能扩展)
按照Python官方扩展规范编写C代码,编译后可直接作为Python模块导入使用。
示例扩展代码:
编译为扩展模块后,在Python中直接#include <Python.h> static PyObject* add_c(PyObject* self, PyObject* args) { int a, b; // 解析Python传入的参数 if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL; // 返回Python类型的结果 return PyLong_FromLong(a + b); } // 方法映射表 static PyMethodDef TestMethods[] = { {"add", add_c, METH_VARARGS, "计算两个整数的和"}, {NULL, NULL, 0, NULL} // 结束标记 }; // 模块定义 static struct PyModuleDef testmodule = { PyModuleDef_HEAD_INIT, "test", // 模块名 NULL, -1, TestMethods }; // 模块初始化入口 PyMODINIT_FUNC PyInit_test(void) { return PyModule_Create(&testmodule); }import test,调用test.add(1,2)即可。 - Cython方案(兼顾开发效率和性能)
用类Python的语法编写逻辑,自动转译为C代码并编译为Python扩展,适合需要同时和C、Python代码做复杂交互的场景。
场景2:C代码调用Python侧定义的函数
原理是在C程序中嵌入Python解释器,加载Python模块后调用目标函数。
示例步骤:
- 先编写Python模块
test_py.py:
def add(a, b): return a + b
- 编写C调用代码:
#include <Python.h> #include <stdio.h> int main() { // 初始化Python解释器 Py_Initialize(); if (!Py_IsInitialized()) { return -1; } // 将当前目录加入Python模块搜索路径 PyRun_SimpleString("import sys; sys.path.append('.')"); // 导入Python模块 PyObject* p_module = PyImport_ImportModule("test_py"); if (!p_module) { Py_Finalize(); return -1; } // 获取目标函数对象 PyObject* p_func = PyObject_GetAttrString(p_module, "add"); if (!p_func || !PyCallable_Check(p_func)) { Py_XDECREF(p_func); Py_XDECREF(p_module); Py_Finalize(); return -1; } // 构造传入参数 PyObject* p_args = PyTuple_New(2); PyTuple_SetItem(p_args, 0, PyLong_FromLong(1)); PyTuple_SetItem(p_args, 1, PyLong_FromLong(2)); // 调用Python函数 PyObject* p_res = PyObject_CallObject(p_func, p_args); int res = PyLong_AsLong(p_res); printf("调用结果:%d\n", res); // 输出3 // 释放所有Python对象引用,避免内存泄漏 Py_XDECREF(p_res); Py_XDECREF(p_args); Py_XDECREF(p_func); Py_XDECREF(p_module); // 关闭Python解释器 Py_Finalize(); return 0; }
- 编译C程序(以Linux为例,替换3.x为你的Python版本,比如3.10):
gcc main.c -o main -I/usr/include/python3.x -lpython3.x
通用注意事项
- 两种语言交互时必须做显式类型转换,C的基础类型和Python对象不直接兼容
- 使用Python C API时注意管理引用计数,否则会出现内存泄漏或程序崩溃
- 编译动态库和二进制程序时要和Python的架构(32位/64位)、版本匹配
内容的提问来源于stack exchange,提问作者EVI FELDI
相关产品推荐
相关产品推荐

