封裝打包Python的好處,節省了安裝各類各樣包依賴的問題,同時能夠增強咱們代碼隱私的安全性,這裏個人演示環境是Python3.6 ,CentOS7的系統,同時打包工具採用pyinstaller。python
默認Python模塊是私有的,咱們想打包就須要將咱們的so模塊變爲共享的,那麼咱們須要執行兩個操做便可。linux
--enable-shared
[root@c7-work-1 ~]# cat /etc/ld.so.conf.d/python3.6.conf /usr/local/python3.6.5-shared/lib [root@c7-work-1 ~]# ldconfig
完成上面操做便可了。安全
pip install pyinstaller
這裏咱們只須要準備工程和相關依賴包,而且安裝成功便可,程序能正常跑則沒問題。bash
這裏根據本身經驗來便可了,沒啥技巧。編輯器
默認狀況下,咱們打包後有些代碼原有的獲取方式會有些轉變。工具
當應用程序運行時,可能須要訪問如下三個常規位置中的任何位置的數據文件:測試
在Python中咱們須要注意提防以下:ui
P1: 使用__file__
和sys._MEIPASS
spa
當程序未凍結時,標準Python變量__file__是如今正在執行的腳本的完整路徑。當捆綁的應用程序啓動時,引導加載程序會設置sys.frozen屬性並將絕對路徑存儲到bundle文件夾中sys._MEIPASS。對於單文件夾捆綁包,這是該文件夾的路徑,不管用戶在哪裏放置它。對於單文件包,這是 引導加載程序建立的臨時文件夾的路徑(請參閱單文件程序的工做原理)。_MEIxxxxxx命令行
P2: 使用sys.executable
和sys.argv[0]
當正常的Python腳本運行時,sys.executable
是執行程序的路徑,即Python解釋器。在一個凍結的應用sys.executable
程序中,也是執行程序的路徑,但這不是Python; 它是單文件應用程序中的引導加載程序或單文件夾應用程序中的可執行文件。這爲您提供了一種可靠的方法來查找用戶實際啓動的凍結可執行文件。
值sys.argv[0]
是用戶命令中使用的名稱或相對路徑。它多是相對路徑或絕對路徑,具體取決於平臺以及應用程序的啓動方式。
下面是一個演示代碼,有興趣能夠執行下看看效果:
#!/usr/bin/python3 import sys, os frozen = 'not' if getattr(sys, 'frozen', False): # we are running in a bundle frozen = 'ever so' bundle_dir = sys._MEIPASS else: # we are running in a normal Python environment bundle_dir = os.path.dirname(os.path.abspath(__file__)) print( 'we are',frozen,'frozen') print( 'bundle dir is', bundle_dir ) print( 'sys.argv[0] is', sys.argv[0] ) print( 'sys.executable is', sys.executable ) print( 'os.getcwd is', os.getcwd() )
根據上面的規則,着重調整一下代碼。
原代碼:
BASE_PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
New代碼:
if getattr(sys, 'frozen', False): BASE_PATH = sys._MEIPASS else: BASE_PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
只須要啓動入口腳本調整便可,其餘腳本無需調整
打包:
pyinstaller thunder_predict.spec
如何生成 *.spec文件呢,能夠這裏直接pyinstaller pyscript便可,而後在修改調整;下面提供個人實例文件:
# -*- mode: python -*- block_cipher = None a = Analysis(['predict/thunder_predict.py'], pathex=['.'], binaries=[], datas=[('model', 'model'), ('dataset', 'dataset')], hiddenimports=['cython','sklearn','sklearn.ensemble','sklearn.neighbors.typedefs', 'sklearn.neighbors.quad_tree','sklearn.tree._utils','scipy._lib.messagestream'], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, [], exclude_binaries=True, name='thunder_predict', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, console=True ) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='thunder_predict')
咱們爲何要打包成爲so文件呢?
Python有py、pyc、pyw、pyo、pyd等文件格式。
其中,pyc是二進制文件。但很容易被反編譯。
pyw也不行,只是隱藏命令行界面而已,能夠做爲入口腳本。
pyo和pyc差很少,也容易被反編譯。
最後剩下pyd格式。pyd格式是D語言(C/C++綜合進化版本)生成的二進制文件,實際也會是dll文件。該文件目前位置沒找到能夠被反編譯的消息,只能被反彙編。Sublime text編輯器也是使用該格式。
打包生成so文件:
python3 ../utils/build_so.py build_ext --inplace
清理無用的文件:
rm -fr build main.c main.py
測試(成功測試成功):
[root@c7-work-1 thunder]# python3 predict/thunder_predict.py 01:dXBsb2FkRGF0YTE1MzI2Nzk4MzQzNDI= 199 success
Cython腳本內容:
from distutils.core import setup from Cython.Build import cythonize """ python build_so.py build_ext --inplace """ setup( name = 'main', ext_modules = cythonize("main.py"), )
命令以下:
[root@c7-work-1 thunder]# pyinstaller thunder_predict.spec 36 INFO: PyInstaller: 3.4 37 INFO: Python: 3.6.5 ................ 省略一下信息
咱們檢查一下是否已經將咱們的So庫文件打包。
[root@c7-work-1 thunder]# ls dist/thunder_predict/main.cpython-36m-x86_64-linux-gnu.so dist/thunder_predict/main.cpython-36m-x86_64-linux-gnu.so
完美。
Q: pyinstaller利用獲取腳本中import來來打包對應的庫,若是你打包成so文件了還能夠?
A: 這裏我經過Python PKG的方式,將so會差異的庫放入到__init__.py中,就能夠解決這個問題。