0、引言
Python在基础开发、数据科学、人工智能、Web框架开发等领域具有广泛的支持工具和开发教程,极大的缩短了产品原型开发周期、降低了开发难度。
有许多的功能,通过C/C++实现,非常的复杂并且不方便,但是Python可能就是几行代码就搞定了。
为了避免处处重复造轮子,又希望在原先的C/C++通用轻松简单的实现一些功能,因此探索在C/C++中如何嵌入调用Python的API及应用程序非常有必要。
1、开发环境搭建
①、Python
本文以当前最新的Python 3.13.3版本为例进行讲解,本教程的Python下载地址如下:
Download Python | Python.orghttps://www.python.org/downloads/
由于Python的版本也一直在更新,如果找查阅到本教程时,最新版本已不是Python3.13.3,可以滑动找到Python的历史版本进行安装。
并在安装完成后,建议通过终端命令行查询一下具体的版本信息:python --version
②、MinGW
简单来说,MinGW是一个编译器,提供了gcc和g++编译工具,可以对C/C++程序代码编译调试。
在之前的很多博客笔记文章中已经对MinGW进行了非常多的讲解了,如果对MinGW编译器有疑惑的,可以通过博主的如下笔记链接,搭建安装MinGW开发环境。
MinGW编译器任意版本使用配置经验教程-CSDN博客https://blog.csdn.net/weixin_49337111/article/details/140274770?spm=1001.2014.3001.5502
③、CMake
CMake(Cross-platform Make)是一款开源的跨平台构建系统生成工具,广泛应用于C、C++、Fortran等编程语言的项目构建中。
在VScode,可以直接安装VScode插件构建工具,即可实现在VScode中使用CMake。
提醒:在一些环境中,可能无法正常使用CMake插件,需要下载最新的CMake程序软件才能辅助使用。
2、工程代码配置
在上面的开发环境搭建好之后,当在程序代码中加入Python相关的头文件时,大概率会出现如下情况,找不到相关头文件,并且编译会直接报错。
上面这个情况也就是说明,并没有和Python建立联系,需要我们自己将Python的路径加入到工程代码的配置中。
通过python --path命令,即可找到python的实际安装路径
python实际的文件所在路径,include包含了C/C++中需要使用的xxx.h头文件。
在libs中提供的是_tkinter.lib、python3.lib、python313.lib
如果在MinGW中不能直接使用xxx.lib库文件,要进行文件格式转换,对于没有接触过了开发者,可能处理起来较为麻烦,如有需要,可以访问博主在下面这篇文章中提到的方法。
Windows中xxx.dll动态链接库文件转xxx.a静态库文件-CSDN博客https://blog.csdn.net/weixin_49337111/article/details/147171368?sharetype=blogdetail&sharerId=147171368&sharerefer=PC&sharesource=weixin_49337111&spm=1011.2480.3001.8118
如果觉得不方便,可以采取直接在Visual Studio程序中包含xxx.lib文件,同样可以实现C/C++中嵌入Python程序进行开发。
在完成上面的操作后,即可创建C/C++工程了,然后配置CMakeLists.txt文件。博主在一系列试错了,最终成功运行的文件如下:
CMakeLists.txt
cmake_minimum_required(VERSION 3.10.0)
project(convert VERSION 0.1.0 LANGUAGES C CXX)
# 设置Python路径
set(PYTHON_INCLUDE_DIR "C:/Users/Administrator/AppData/Local/Programs/Python/Python313/include")
set(PYTHON_LIBRARY "C:/Users/Administrator/AppData/Local/Programs/Python/Python313/libs/python313.lib")
# 添加可执行文件
add_executable(convert main.cpp)
# 包含Python头文件目录
target_include_directories(convert PRIVATE ${PYTHON_INCLUDE_DIR})
# 链接Python库
target_link_libraries(convert PRIVATE ${PYTHON_LIBRARY})
# 在Windows上需要定义PYTHON_LIBRARY宏
if(WIN32)
target_compile_definitions(convert PRIVATE PYTHON_LIBRARY)
endif()
提醒:如果在Windows环境中,发现存在MSVC会和当前MinGW编译器混用,建议直接换更新版本的MinGW,经过反复试错,最终得出来的正确解决办法,惨痛的教训!!!
在编译正常运行后,说明环境搭建完毕,工程配置正常,可以开始C/C++中正常调用Python进行开发了。
3、C/C++中调用Python
对于在C/C++中如何使用Python的API接口,Python官方已经出来非常详细的教程,有需要的博客朋友可以访问如下链接对该教程参考学习。
Python的C/C++外部扩展官方教程:
1. Embedding Python in Another Application — Python 3.13.3 documentationhttps://docs.python.org/3.13/extending/embedding.html
①、常用API接口
(1)初始化 Python 解释器
//用于初始化 Python 解释器,它会设置 Python 运行所需的环境,加载内置模块等。
void Py_Initialize(void);
(2)终止 Python 解释器
//程序结束时,要调用此函数来终止解释器,释放相关资源。
void Py_Finalize(void);
(3)命令行参数处理
//将 C/C++ 程序的命令行参数传递给 Python 解释器。
void PySys_SetArgv(int argc, char **argv);
(4)执行Python简单语句
//使用此函数接口可以在 C/C++ 代码中执行简单的 Python 语句。
int PyRun_SimpleString(const char *command);
(5)执行 Python 文件
//使用此函数接口,可以在C/C++代码中间接执行 Python 文件
int PyRun_SimpleFile(FILE *fp, const char *filename);
(6)导入 Python 模块
//此函数接口,可以实现导入 Python 模块。
PyObject* PyImport_ImportModule(const char *name);
(7)调用 Python 函数
//用于在 C/C++ 中调用 Python 的可调用对象(函数、方法、类等)。
PyObject* PyObject_CallObject(PyObject *callable, PyObject *args);
②、程序实例
(1)Python语句执行
// C/C++测试 HTTP 请求和 HTML 解析功能效果如下:
#include <Python.h>
#include <iostream>
#include <string>
#include <stdexcept>
class PythonHttpParser {
public:
PythonHttpParser() {
Py_Initialize();
// 确保 requests 和 bs4 可用
PyRun_SimpleString(
"import sys\n"
"try:\n"
" import requests\n"
" from bs4 import BeautifulSoup\n"
"except ImportError as e:\n"
" print(f'Error: {e}')\n"
" sys.exit(1)\n"
);
}
~PythonHttpParser() {
Py_Finalize();
}
std::string fetch_title(const std::string& url) {
PyObject *pFunc, *pArgs, *pResult;
// 获取 main 模块
PyObject* main_module = PyImport_AddModule("__main__");
PyObject* global_dict = PyModule_GetDict(main_module);
// 准备 Python 代码
const char* code =
"def get_page_title(url):\n"
" try:\n"
" response = requests.get(url)\n"
" soup = BeautifulSoup(response.text, 'html.parser')\n"
" return soup.title.string if soup.title else 'No title found'\n"
" except Exception as e:\n"
" return f'Error: {str(e)}'\n";
PyRun_SimpleString(code);
// 获取函数
pFunc = PyDict_GetItemString(global_dict, "get_page_title");
if (!pFunc || !PyCallable_Check(pFunc)) {
throw std::runtime_error("Failed to get Python function");
}
// 准备参数
pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, PyUnicode_FromString(url.c_str()));
// 调用函数
pResult = PyObject_CallObject(pFunc, pArgs);
if (!pResult) {
PyErr_Print();
throw std::runtime_error("Python function call failed");
}
// 获取结果
std::string result = PyUnicode_AsUTF8(pResult);
// 清理
Py_DECREF(pArgs);
Py_DECREF(pResult);
return result;
}
};
int main() {
try {
PythonHttpParser parser;
std::string title = parser.fetch_title("https://en.wikipedia.org/wiki/Main_Page");
std::cout << "Page title: " << title << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
C/C++测试 HTTP 请求和 HTML 解析功能效果如下:
(2)Python代码文件调用
#include <Python.h>
#include <cstdio>
#include <cstdlib>
int main(int argc, char* argv[]) {
// 初始化 Python 解释器
Py_Initialize();
// 创建一个 wchar_t** 数组来存储转换后的命令行参数
wchar_t** wargv = (wchar_t**)malloc(argc * sizeof(wchar_t*));
if (!wargv) {
perror("Failed to allocate memory");
Py_Finalize();
return 1;
}
for (int i = 0; i < argc; ++i) {
wargv[i] = Py_DecodeLocale(argv[i], nullptr);
if (!wargv[i]) {
perror("Failed to decode argument");
for (int j = 0; j < i; ++j) {
PyMem_RawFree(wargv[j]);
}
free(wargv);
Py_Finalize();
return 1;
}
}
// 将转换后的参数传递给 Python
PySys_SetArgv(argc, wargv);
// 指定要运行的 Python 脚本文件名
const char* script_file = "../script.py";
// 打开 Python 脚本文件
FILE* fp = fopen(script_file, "r");
if (!fp) {
perror("Failed to open file");
for (int i = 0; i < argc; ++i) {
PyMem_RawFree(wargv[i]);
}
free(wargv);
Py_Finalize();
return 1;
}
// 运行 Python 脚本
int result = PyRun_SimpleFile(fp, script_file);
fclose(fp);
// 释放分配的内存
for (int i = 0; i < argc; ++i) {
PyMem_RawFree(wargv[i]);
}
free(wargv);
// 终止 Python 解释器
Py_Finalize();
return result;
}
script.py
import sys
# 打印接收到的命令行参数
print(f"Received arguments: {sys.argv}")
# 检查是否包含 --help 参数
if "--help" in sys.argv:
print("Usage: script.py [--input <file>] [--verbose]")
程序执行结果: