C++与Python混合编程 - 用C++/C调用python
添加python库到环境中
这里的
.lib
要根据实际安装的版本选择对应的名称
YOUR_PYTHON_PATH: python安装路径
include_directories(YOUR_PYTHON_PATH/include)
link_libraries(YOUR_PYTHON_PATH/libs/YOUR_PYTHON_VERSION.lib)
添加头文件 Python.h
#include <Python.h>
Python 初始化
#include <iostream>
#include <Python.h>
using namespace std;
void initPython()
{
Py_Initialize();
if(!Py_IsInitialized())
{
cout << "Fail to init python.\n";
return;
}
// 将当前位置添加到sys中(便于后续importMoudle)
// 如果python文件或 module 在项目的其他文件夹中, 需将文件夹路径添加到sys中(否则会无法导入文件)
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
PyRun_SimpleString("sys.path.append(YOUR_PATH_HERE)")
}
由于后续获取 Python 文件中的函数时不会运行该文件的其他语句(包括 import), 因此需要将依赖的库一同import:
void importPyModule(string moduleName)
{
string pyRunStr = "import ";
pyRunStr += moduleName;
PyRun_SimpleString(pyRunStr.c_str());
}
void importNecessaryModule()
{
// example
importPyModule("os");
importPyModule("math");
// import module you used in .py file here
}
外部库需要将库文件粘贴到项目构建目录下, 库文件存储在 .\Lib\site-packages
中
引入 Python 文件
int main()
{
PyObject* hello = PyImport_ImportModule("hello");
if(!hello)
{
cout << "Fail to load 'hello.py'\n";
return 1;
}
}
运行简单的 Python 语句
直接调用 PyRun_SimpleString()
函数
PyRun_SimpleString("print('hello, world!')");
获取 Python 文件中的函数
以如下 python 文件为例:
# hello.py
import math
def my_op(*nums):
ret = 0
for num in nums:
ret += math.sin(num)
return ret
if __name__ == "__main__":
print(my_op(1, 2, 3, 4, 5))
获取文件中的函数:
PyObject* getPyFunction(PyObject* module, string name)
{
PyObject* retObj = PyObject_GetAttrString(module, name.c_str());
if(!retObj)
{
cout << "Fail to load function: " << name << endl;
}
return retObj;
}
获取变量等同理
代码如下:
int main()
{
PyObject* hello = PyImport_ImportModule("hello");
if(!hello)
{
cout << "Fail to load 'hello.py'\n";
return 1;
}
PyObject* myOpFromPy = getPyFunction(hello, "my_op");
}
运行 Python 函数
- 调用无参函数:
PyObject* funcRet = PyObject_CallFunction(yourFunction, 0);
- 调用有参数函数:
- 声明一个新的
PyTuple
, 参数为函数参数个数 - 给
PyTuple
的每个索引 用PyObject
赋值 - 调用函数
- 翻译返回值
- 声明一个新的
int main()
{
// 初始化
initPython();
importNecessaryModule();
// import hello 模块
PyObject* hello = PyImport_ImportModule("hello");
if(!hello)
{
cout << "Fail to load 'hello.py'\n";
return 1;
}
// 获取 my_op 函数
PyObject* myOpFromPy = getPyFunction(hello, "my_op");
double arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int len = 10; // arr.size()
// 声明一个新的 `PyTuple` , 参数为函数参数个数
PyObject* args = PyTuple_New(len);
// 给 `PyTuple` 的每个索引 用 `PyObject` 赋值
for(int i = 0; i < len; i++)
{
PyObject* curr = PyFloat_FromDouble(arr[i]);
PyTuple_SetItem(args, i, curr);
}
// 调用函数
PyObject* funcRet = PyObject_CallObject(myOpFromPy, args);
// 翻译返回值
double ans = 0;
PyArg_Parse(funcRet, "d", &ans);
cout << "my_op from python: output: " << ans << endl;
return 0;
}
也可以使用可变参数写出一个调用 python 函数的函数
PyObject* runPyFunction(PyObject* function, int argsNum, const char* format, ...)
{
va_list args;
va_start(args, format);
PyObject* funcArgs = PyTuple_New(argsNum);
// current arg
PyObject* curr = NULL;
int counter = 0;
while (*format != '\0' && counter < argsNum) {
if (*format == '%') {
format++; // 移动到占位符的下一个字符
if (*format == 'd') {
curr = PyLong_FromLong(va_arg(args, int));
PyTuple_SetItem(funcArgs, counter, curr);
} else if (*format == 'f') {
curr = PyFloat_FromDouble(va_arg(args, float));
PyTuple_SetItem(funcArgs, counter, curr);
} else if (*format == 's') {
curr = PyUnicode_FromString(va_arg(args, char*));
PyTuple_SetItem(funcArgs, counter, curr);
}else {
printf("Unsupported format specifier: %c", *format);
return NULL;
}
counter++;
}
format++; // 移动到下一个字符
}
va_end(args);
PyObject* retObj = PyObject_CallObject(function, funcArgs);
return retObj;
}
然后可以直接这样使用:
PyObject* funcRet = runPyFunction(myOpFromPy, 4, "%d%d%d%d", 1, 2, 3, 4);
double ans = 0;
PyArg_Parse(funcRet, "d", &ans);
cout << "my_op from python: output: " << ans << endl;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!