写了个吊炸天的 Python 项目, 把我和左手相处的时间都赔上了. 但出于版权考虑, 我不太想让使用方直接用我的代码, 毕竟 Python 代码给出去, 就真的收不回来了.
想给客户演示的时候, 不想那么墨迹的打开 dos cmd 或者 terminal , 然后运行 python App.py 这样的命令行. 最好是客户双击, 完事儿. 就像有人在那自己动一样......
PyInstaller 来了, 他就是这么一款帮助我们把整个项目完整打包的工具. 目前已经兼容 Py3.7, 以及 Mac App 和 Windows Exe.
文档: https://pyinstaller.readthedo/ ...
先说下, 这篇文章有别于网上那坨安装, 打包的草包, 这次是真核!
1. 安装
这个很简单, 直接 pip install pyinstaller 就好.
?? 注意了: 你要编译成 exe, 建议你省心点的在 Windows 上用 pyinstaller, 如果你要 Mac App 的, 那就用 Mac 编译.
我今天就以 Windows 为例
2. 简单使用
这个也很简单, 网上一抓一大把, 我这里就不赘述了, 无非就是那么几个命令:
pyinstaller -F 项目主文件 (或者是单一脚本)
3. 参数说明
-F, 打包所有的依赖包在一个 exe 中, 包括你自己的模块, 内置模块以及第三方模块.
-c, 如果你是命令行窗口, 就要加上这个参数.
-w, 窗口程序, 比如你用了 PyQt.
4. 高级用法: 配置文件
.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 用处最多, 一个个解释:
第一个参数, 是指定我们整个项目的主程序, 也就是我们的入口文件.
pathex, 就是我们的工作目录
datas, 存放我们的数据.
好了, 说到这里就要好好说一说这个 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
5. 临时目录
那刚刚说的临时目录在代码里怎么处理呢, 如果代码中还是老样子处理相对路径, 一定是找不到的.
官方文档中给出了这么一段:
- 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 时候的临时目录.
这对代码的改动是最小的.
6. 编译打包
最后, 我们执行 python xxx.spec 就好了. 打包的可执行文件会在 dist 里, build 中是一些打包时候需要的文件.
输出中最后有 successfully 字样, 就算成功了. 他也会告诉你, exe 出现在哪个位置.
当然不是说这样就万无一失了, 别人也可以反编译你的 exe, 所以我们可以在打包的时候用 Cython 去编译一次, 把混淆过的代码打包. 这样的话难度就增加了, 同时再加上 Mac 地址绑定, 这里就有多种思路了.
相信大家在学 python 的时候肯定会遇到很多难题, 以及对于新技术的追求, 这里推荐一下我们的 Python 学习扣 qun:784758214, 这里是 python 学习者聚集地!! 同时, 自己是一名高级 python 开发工程师, 从基础的 python 脚本到 web 开发, 爬虫, django, 数据挖掘等, 零基础到项目实战的资料都有整理. 送给每一位 python 的小伙伴! 每日分享一些学习的方法和需要注意的小细节
走进: python 技术分享交流 http://0x9.me/9LBJY
来源: http://www.bubuko.com/infodetail-3054297.html