C++ python 混合编程 + 软件发布教程

环境

vs 2017
win10 x64
anaconda3 python 3.7.0

C++、Python 混合编程

Python调用C++

官方文档:Extending Python with C or C++

相关链接:
Python与C交互之指针

C++调用Python

官方文档:Embedding Python in Another Application

C++项目配置:
(1)头文件路径:C:\ProgramData\Anaconda3\include

#include <Python.h>

(2)Lib路径:C:\ProgramData\Anaconda3\libs

#pragma comment(lib, "_tkinter.lib")
#pragma comment(lib, "python3.lib")
#pragma comment(lib, "python37.lib")

(3)Dll路径:C:\ProgramData\Anaconda3
将"python37.dll"拷贝至C++程序运行目录

(4)第三方库的目录:C:\ProgramData\Anaconda3\Lib\site-packages
若python代码里调用了第三方库,需要将库的依赖项拷贝至C++程序目录

例子:


软件发布

python代码的发布方法有几种。

方法一、setup.py
为自己编写的python模块编写setup.py文件,在目标机器上通过

python setup.py install

即可部署自己的模块。如果模块依赖另外的库,只要在setup.py中声明好,也可以同时部署。
该方法需要联网,在执行setup.py脚本的同时会自动pip安装所需的包。
该方法的好处是按需部署,只安装所需的库,而接下来的方法二则是python环境的整体部署。

Python 库打包分发(setup.py 编写)简易指南
python的构建工具setup.py

方法二、本地python环境整体部署

通过 pip freeze > requestments.txt获取依赖,然后执行

pip download  -r requestments.txt  -d  ./packages

在本地环境下先把包下载好,之后拷贝至目标环境,执行

pip install --no-index --find-links=packages_path -r requirements.txt 

当然,也可以手动删掉某些包,从而实现跟方法一的按需部署。

python如何离线装包 离线如何部署python环境

方法三、pyinstaller生成可执行文件

该方法可以实现脱离python环境的部署,如windows下,就将python脚本转换为exe文件。
但是转换速度比较慢,随着依赖库越多,转换exe的过程也越长。而且,只能运行在跟本地环境一致的操作系统。

github FAQ
pyinstaller打包异常
python利用pyinstaller打包简明教程

方法四、半自动打包

第一步,通过sys.modules获取python脚本的依赖项
编写extract.py,将待发布的.py脚本,如mytest.py,里的所有import拷贝至extract.py的开头,然后按照下面的内容编写

# 将待发布的.py脚本里的所有import拷贝至此处
# For example :
#  import cv2
#  import numpy

import sys
import os
import shutil

def getModulesPath():
    lst = []
    #sys.modules是一个字典,数据格式如下:
    #{'site': <module 'site' from 'D:\Python27\lib\site.pyc'>,
    for v in sys.modules.values() :
        s = str(v)
        if "from" in s:
            data = s.split("'")
            modulepath = data[-2]  
            modulepath = eval(repr(modulepath).replace('\\\\', '\\'))   #实测split方法输出的字符串中,双反斜杠会变成四反斜杠,要替换为双反斜杠,而且用replace是不行的
            lst.append(modulepath)
        else :
            print("module : ", s)
    return lst

def extractFiles(destDir, files):
    destDir.replace("/", "\\")
    if destDir[-1] != '\\' :
        destDir += '\\'

    for f in files :
        dest = filiterPath(destDir, f)
        copyF(dest, f)

def filiterPath(destDir, srcFile):
    dest = destDir
    maxLen = 0
    for path in sys.path:
        lenp = len(path)
        if lenp < len(srcFile): # and path == srcFile[:lenp]:
            pp = srcFile[:lenp]
            if path == srcFile[:lenp]:
                if maxLen < lenp:
                    dest = destDir + srcFile[lenp+1:]
                    maxLen = lenp

    dest.replace("/", "\\")
    if '.' in dest: #去掉文件名
        p = dest.rfind('\\')
        if p >= 0:
            dest = dest[:p]
    return dest

def copyF(destDir, srcFile):
    if not os.path.isfile(srcFile):
        print("error : file %s not exist!" % srcFile)
        return False
    if not os.path.isdir(destDir):
        os.makedirs(destDir)
        print("make dir:", destDir)
    try:
        shutil.copy2(srcFile, destDir)
        print("copy file : %s to %s" % (srcFile, destDir))
    except IOError:
        print("error : copy %s to %s faild" % (srcFile, destDir))
        return False
    return True

def extract():
    a = getModulesPath()
    extractFiles("libs\\", a) 

extract()

运行extract.py,相关的依赖项会自动拷贝至libs目录下

第二步,将mytest.py拷贝至刚才得到的libs里,运行mytest.py

第三步,运行mytest.py可能会报错,这是因为第一步没有完全获取所有的依赖项,此时就要根据命令行输出的错误信息,找出有问题的第三方包,从anaconda的site-package里将这些包手动复制过来,最后就可以正常运行mytest.py
另外,上述方法获得的依赖项一大堆,可能存在没有用的,我们可以通过查看代码中import的包的依赖项来判断哪些文件是多余的,这样做的意义在于,某些库可能是GPL或者商业license,会对项目造成影响,当然这样还能节省空间。

该方法要求在目标机器上安装对应版本的python,然后将上面获得的所有文件拷贝到目标机器上即可运行


异常相关

避免程序运行完命令行窗口关闭,可以通过在最后加一句os.system("pause"),这样就可以查看异常信息

问题:C++调用python,debug运行报错

Debug Assertion Failedl
Program: D:\Debug mytest.exe
File: minkernel\crts\ucrt\sre\appcrt\heap\debug _heap.cpp
Line: 904
Expression: _CrtlsValidHeapPointer(block)

release下没有报错
暂且无视该问题

问题:pyinstaller打包异常

问题:解决PyRun_SimpleFile/PyRun_SimpleString报错

问题:出现importlib_metadata.PackageNotFoundError: importlib_metadata异常,解决办法见解决PackageNotFoundError:XXXX异常

问题:重复调用Py_Finalize、Py_Initialize导致的内存写入异常

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章