寫了個吊炸天的Python項目,把我和左手相處的時間都賠上了。但出於版權考慮,我不太想讓使用方直接用個人代碼,畢竟Python代碼給出去,就真的收不回來了。html
想給客戶演示的時候,不想那麼墨跡的打開dos cmd 或者 terminal ,而後運行 python app.py 這樣的命令行。最好是客戶雙擊,完事兒。就像有人在那本身動同樣……python
PyInstaller 來了,他就是這麼一款幫助咱們把整個項目完整打包的工具。目前已經兼容Py3.7,以及 Mac App 和 Windows Exe。windows
文檔:https://pyinstaller.readthedocs.io/en/stable/index.htmlapp
先說下,這篇文章有別於網上那坨安裝、打包的草包,此次是真核!工具
這個很簡單,直接 pip install pyinstaller 就好。ui
⚠️注意了:你要編譯成exe,建議你省心點的在windows上用pyinstaller,若是你要mac app的,那就用mac編譯。命令行
我今天就以windows爲例code
這個也很簡單,網上一抓一大把,我這裏就不贅述了,無非就是那麼幾個命令:htm
pyinstaller -F 項目主文件(或者是單一腳本)對象
-F,打包全部的依賴包在一個exe中,包括你本身的模塊、內置模塊以及第三方模塊。
-c,若是你是命令行窗口,就要加上這個參數。
-w,窗口程序,好比你用了PyQt。
.spec,這個文件很是重要,咱們能夠經過編輯這個文件來打包咱們的項目,相似DockerFile。
我給你們貼一個個人:
# -*- mode: python -*- block_cipher = None a = Analysis(['C:\\app\\main.py'], pathex=['C:\\'], binaries=[], datas=[ ('C:\\data\\input\\builtin\\*.xlsx', '.\\data\\input\\builtin\\'), ('C:\\data\\input\\*.xlsx', '.\\data\\input\\'), ('C:\\data\\output\\', '.\\data\\output\\'), ('C:\\log\\', '.\\log\\'), ('C:\\app\\db\\', '.\\app\\db\\') ], hiddenimports=['numpy', 'pandas'], ... ) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, ... )
這其實就是一個python文件,只不事後綴是spec罷了。
.spec一共會有4個對象,分別是:Analysis、PYZ、EXE、COLLECT。
Analysis用處最多,一個個解釋:
好了,說到這裏就要好好說一說這個Pyinstaller的工做流程了。當咱們雙擊編譯好的exe後,他是會建立一個臨時目錄,把全部須要用的包都解壓到那裏,而後執行。執行完畢後,臨時文件夾就消失了。
這和咱們有什麼關係呢?想一下,若是你的項目中須要去讀取某些文件,甚至是用戶的輸入參數,怎麼辦?打包出來的exe 是沒有辦法經過直接指定參數,相似:python main.py --input=*.xlsx 來讀取文件的,由於我以前說了,在執行的時候會把項目解壓到一個臨時目錄,因此原來項目中寫好的相對路徑也無論用。
爲此,咱們須要把host上的實際文件給copy到那個臨時目錄下,因此這個datas的做用就是這個,個人文件中,我把host下的 C:\data\input\builtin\*.xlsx文件都copy到臨時目錄的 data\input\builtin 下面。
hiddenimports ,繼續說下去,PyInstaller有時候沒法偵察到所有的依賴包,怎麼辦?咱們能夠在這個後面加,把PyInstaller編譯出來的exe在運行的時候報的缺乏模塊給寫裏面。
⚠️注意了: pandas 和 numpy 有個很奇怪的地方,就是引用了 pandas 的地方,若是沒有引用 numpy ,就會報錯。因此你能夠在主入口上面加一個 import numpy 。
⚠️注意了:直接 import numpy 仍是會報錯。怎麼辦?在 import numpy 下面加 import numpy.core._dtype_ctypes
那剛剛說的臨時目錄在代碼裏怎麼處理呢,若是代碼中仍是老樣子處理相對路徑,必定是找不到的。
官方文檔中給出了這麼一段:
Your app should run in a bundle exactly as it does when run from source. However, you may need to learn at run-time whether the app is running from source, or is 「frozen」 (bundled).
import sys if getattr( sys, 'frozen', False ) : # running in a bundle basedir = sys._MEIPASS else : # running live
因此在你的項目中,若是有配置文件的話,就在那裏加上這一段,而後在bundle中添加你的新路徑,else仍是你的老代碼。
這個 sys._MEIPASS 是個特殊的值,是在Pyinstaller打包的時候纔會添加的臨時變量,經過這個變量咱們能夠獲取到在執行exe時候的臨時目錄。
這對代碼的改動是最小的。
最後,咱們執行 python xxx.spec 就行了。打包的可執行文件會在dist裏,build中是一些打包時候須要的文件。
輸出中最後有 successfully 字樣,就算成功了。他也會告訴你,exe出如今哪一個位置。
固然不是說這樣就萬無一失了,別人也能夠反編譯你的exe,因此咱們能夠在打包的時候用Cython去編譯一次,把混淆過的代碼打包。這樣的話難度就增長了,同時再加上mac地址綁定,這裏就有多種思路了。下一次我給你們說說。
關注公衆號「Python專欄」,回覆關鍵字:「spec」獲取完整項目spec文件。
![Python專欄二維碼](https://img-blog.csdnimg.cn/20190228220401806.jpeg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIzNjU4Mjg=,size_4,color_FFFFFF,t_70#pic_center =200x200)