環境
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異常