python有py、pyc、pyw、pyo、pyd等文件格式,其中除了py的其他格式都起到了不暴露源码的作用,但是pyc、pyw、pyo容易被反编译,所以就剩pyd格式,该文件格式不会被反编译。本文将py文件生成pyd文件,并通过c++调用的具体过程进行介绍。
一、准备环境
1、Microsoft Visual Studio(2019或者2022都可以),没有安装过的看这里,传送门
2、安装过anaconda3,没有安装过的看这里,传送门
3、只安装对应的VS和anaconda3软件,不安装其他环境(记住anaconda3的安装目录,比如我的安装目录在D:\Users\WDX\anaconda3)
4、网络良好
二、通过anaconda安装虚拟环境
1、点击电脑左下角的开始,然后再搜索栏中输入Anaconda Prompt,找到该应用,点击打开。
2、打开后,按照这篇文章中的anaconda操作方式创建新的虚拟环境,或者自己输入如下指令,名字为runLib(根据自己的需求),python环境为3.8。
conda create -n runLib python=3.8
3、然后等待环境创建,出现" Processed([y]/n)?",输入“y”;等待环境创建成功。
4、激活环境,命令如下
conda activate runLib
5、不要关闭窗口,先放一放,进行其他操作。
三、准备python文件
1、通过pycharm或者python编译器,编写一个python脚本,文件名命名为testPy(注意不能为test,因为python内置有test.py),代码如下,这里我们导入numpy第三方包。(一会通过anaconda安装这个包)
import sys
import numpy as np
print('enter python success')
def Hello():
print("Hello, world")
def test_cpp2py(data):
data_after_np = np.array(data)
print('successful, and data is: ', data_after_np) # 输出
return [1, 2, 3, 4]
if __name__ == "__main__":
Hello()
data = [6,6,6,6]
test_cpp2py(data)
2、再创建一个python文件,命名为setup,这里我们导入Cython包,(一会通过anaconda安装这个包)代码如下
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize("testPy.py") # 这里改为你要打包成.pyd文件的py文件路径
)
3、假如我刚才创建的文件路径在,这个里面。
4、重点来了,打开第二步打开的命令窗口,在窗口中输入E:,(因为我的文件在E:\code\python\pyd放着),回车。当变成下面这样时,我们再输入cd E:\code\python\pyd,如下图所示,即为已进入目录下。
5、然后输入pip install cython,安装cython库。
6、安装完成后,继续安装numpy第三方库,这里要注意,我们的python版本要与numpy库对应,如果直接安装,默认安装最高版本,最后再调用的时候会出现加载不上库(这是个大坑)。参考这两篇博文后,与python3.8对应的numpy版本为numpy1.18.2,需要通过下面地址下载。
下载地址:https://pypi.org/project/numpy/1.18.2/#files
7、下载完成后,需要进行安装。我把文件放在了F盘。所以安装的时候只需要在命令框中输入pip install F:\numpy-1.18.2-cp38-cp38-win_amd64.whl,回车.出现如下所示时,表示已正确安装。
8、然后我们需要在命令框中输入,python setup.py build_ext --inplace,回车。等待执行命令,生成.pyd文件。
python setup.py build_ext --inplace
9、改名,将testPy.cp38-win_amd64.pyd,改名成testPy.pyd。
四、编写c++调用程序
1、打开vs,创建控制台应用,选择release,x64;然后将下面代码覆盖进去。
#include <iostream>
#include <Python.h>
#include <vector>
using namespace std;
vector<int> test_param2py(vector<int> data) {
// 在这里导入你的 .pyd 文件,注意一定不要加后缀,比如testForCpp2Py.cp310-win_amd64.pyd就写testForCpp2Py就行
PyObject* pModule = PyImport_ImportModule("testPy");
// 声明一个用于接收结果的数组
vector<int> result{};
// 下面实现对.pyd文件中的函数的操作(前提是前面的操作没问题,pModule不会是空指针
if (pModule != nullptr) {
PyObject* pFunc = PyObject_GetAttrString(pModule, "test_cpp2py"); // 获取函数,这里填写要调用的函数名,笔者这里是test_cpp2py
if (pFunc && PyCallable_Check(pFunc)) { // 判断该函数是否存在且可调用
// 创建参数,由于c++和python的类型不对应,所以要转化
// 这里根据传入的vector类型的data来创建一个对应列表list
PyObject* pData = PyList_New(data.size());
for (int i = 0; i < data.size(); ++i) {
PyList_SetItem(pData, i, PyLong_FromLong(data[i])); // 这里要对data[i]转化为python类型
}
// 构建一个参数列表,第一个参数为参数列表的参数数量,这里为1
PyObject* pArgs = PyTuple_Pack(1, pData);
// 调用该函数
PyObject* pValue = PyObject_CallObject(pFunc, pArgs); // 调用函数
Py_DECREF(pArgs);
// 处理返回值
if (pValue != nullptr) {
;
// 从pValue中取值
for (Py_ssize_t i = 0; i < PyList_Size(pValue); ++i) {
result.push_back(PyLong_AsLong(PyList_GetItem(pValue, i)));
}
Py_DECREF(pValue);
}
Py_DECREF(pFunc);
}
Py_DECREF(pModule);
}
else {
// 证明有问题,pModule为空指针了
PyErr_Print(); // 打印错误信息
}
return result; // 返回结果
}
int main()
{
Py_SetPythonHome(L"./runLib");
Py_Initialize(); // 初始化 Python 解释器
// 检查初始化是否成功
if (!Py_IsInitialized())
{
printf("初始化失败!");
return 0;
}
//PyRun_SimpleString("print ('hello world')"); //ok
// 无参无返回值
// test_cpp2py_c(); //ok
// 有参有返回值
vector<int> result = test_param2py({ 100, 200, 300, 400 });
cout << "\n返回值为:";
for (int item : result) {
cout << item << ' ';
}
cout << endl;
Py_Finalize(); // 关闭 Python 解释器
system("pause");
2、打开anaconda3的目录,找到envs,再找runLIb目录,也就是我们安装的虚拟环境。找到之后我们把include文件夹和libs文件夹复制在刚才创建的VS C++工程下。
3、右键工程,点击属性,开始配置包含目录和库目录。
4、配置完成后,点击生成。找到生成的exe文件。然后我们将testPy.pyd文件、python38.dll文件放进这个目录下;其中python38.dll是在runLib文件下的python38.dll复制。最后把runLib整个文件夹放在该目录下(因为软件启动需要调用python解释器,这样我们就不需要配置环境变量)