环境
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
当然,也可以手动删掉某些包,从而实现跟方法一的按需部署。
方法三、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下没有报错
暂且无视该问题
问题:解决PyRun_SimpleFile/PyRun_SimpleString报错
问题:出现importlib_metadata.PackageNotFoundError: importlib_metadata
异常,解决办法见解决PackageNotFoundError:XXXX异常