写了个吊炸天的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文件。
