如何在VSCode的C++项目中调用.pyc文件内的Python函数
核心前提说明
你首先需要明确一个认知:.pyc是Python虚拟机专属的预编译字节码,不是可以直接被CPU执行的原生机器码,不存在完全脱离Python运行时环境直接加载.pyc执行的方案。你想要在C中调用.pyc里的函数,本质上是在C进程中嵌入Python运行时,由运行时加载执行字节码,这个方案完全可行,且调用开销很低,可以实现接近无缝的调用体验。
另外你给出的示例Python代码存在逻辑错误:py_compile.compile的执行位置在函数定义之前,且编译目标文件和函数定义所在文件不匹配,会导致生成的.pyc文件不包含目标函数,下文会给出修正后的可运行代码。
方案1:嵌入Python C API 直接加载.pyc调用(匹配你原始需求的首选方案)
这个方案不需要修改你现有的Python密码学逻辑,直接通过Python官方提供的C接口在C++中启动轻量Python解释器,加载你提前编译好的.pyc文件,直接提取函数调用即可,性能损耗极小。
实现步骤
- 第一步:整理并编译Python代码生成有效.pyc文件
首先把密码学逻辑单独存为源文件,再执行编译:
密码学源文件crypto_func.py内容:
编译脚本import hashlib def generate_keys(): public_key = "Please help me".encode("utf-8") alphanumeric_address = hashlib.sha256(public_key).hexdigest() return public_key, alphanumeric_addresscompile_pyc.py内容,运行后会在同目录生成可用的crypto_func.pyc:import py_compile py_compile.compile("crypto_func.py", cfile="crypto_func.pyc") - 第二步:编写C++调用逻辑,链接Python开发库
首先安装对应Python版本的开发包(Linux环境可通过系统包管理器安装python3-dev,Windows环境可直接从已安装的Python目录中获取头文件和库文件),之后编写调用代码:#include <iostream> #include <Python.h> #include <string> int main() { // 初始化Python解释器 Py_Initialize(); if (!Py_IsInitialized()) { std::cerr << "Python解释器初始化失败" << std::endl; return -1; } // 将当前目录加入模块搜索路径,确保能找到pyc文件 PyRun_SimpleString("import sys; sys.path.insert(0, '.')"); // 加载编译好的pyc模块,注意模块名不要带.pyc后缀 PyObject* pModule = PyImport_ImportModule("crypto_func"); if (!pModule) { PyErr_Print(); std::cerr << "加载pyc模块失败,请检查Python版本与pyc编译版本是否一致" << std::endl; Py_Finalize(); return -1; } // 提取模块中的generate_keys函数 PyObject* pFunc = PyObject_GetAttrString(pModule, "generate_keys"); if (!pFunc || !PyCallable_Check(pFunc)) { PyErr_Print(); std::cerr << "获取目标函数失败" << std::endl; Py_DECREF(pModule); Py_Finalize(); return -1; } // 调用无参数函数 PyObject* pReturn = PyObject_CallObject(pFunc, nullptr); if (!pReturn) { PyErr_Print(); std::cerr << "函数执行失败" << std::endl; Py_DECREF(pFunc); Py_DECREF(pModule); Py_Finalize(); return -1; } // 解析返回值元组,提取第二个位置的地址字符串 PyObject* pAddress = PyTuple_GetItem(pReturn, 1); std::string alphanumeric_address = PyUnicode_AsUTF8(pAddress); std::cout << alphanumeric_address << std::endl; // 回收Python对象引用,避免内存泄漏 Py_DECREF(pReturn); Py_DECREF(pFunc); Py_DECREF(pModule); // 关闭Python解释器,释放资源 Py_Finalize(); return 0; }
注意事项
编译C程序时,链接的Python库版本必须和编译.pyc使用的Python大版本完全一致,比如Python3.10生成的.pyc无法被3.9或3.11版本的解释器加载,会报魔数校验错误。
发布程序时需要随C程序一起分发对应Python版本的核心运行时依赖,以及pqcrypto模块的依赖文件。
方案2:不依赖Python运行时的替代方案
如果你不想在C++程序中携带Python运行时,可以选择以下几种方案实现相同的调用效果:
- 用Cython将Python代码编译为原生动态库:通过Cython给Python代码加少量类型声明,将包括pqcrypto在内的逻辑直接转译为C/C代码,编译为系统原生的动态/静态库,C可以直接链接调用,完全不需要Python环境,运行性能接近原生C++代码,缺点是需要适配部分Python动态特性的类型声明。
- 本地服务化调用:将Python密码学逻辑封装为本地服务,通过本地套接字、管道、gRPC等方式和C进程通信,C按照约定协议发送参数、接收返回值即可,优点是完全不侵入两边的编译环境,适配成本低,缺点是存在跨进程通信的性能开销,适合调用频率不高的场景。
- 替换为C原生的后量子密码实现:你使用的pqcrypto模块本质是对后量子密码算法原生C实现的Python封装,可以直接替换为对应的C原生后量子密码库,完全跳过Python层,性能最好,长期维护成本最低。
内容的提问来源于stack exchange,提问作者user19316228
相关产品推荐
相关产品推荐

