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導致的內存寫入異常

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