PyInstaller 飞速入门
PyInstaller 把 python 项目打包成可执行文件供传播。
安装
PyInstaller 自己也是一个 pip 模块,使用 pip 去安装:
1 |
|
使用
pyinstaller 只需要传给它项目的入口文件,
1 |
|
pyinstaller 自己会去解析代码中的依赖并打包进去。这里有一个很显然的地方——动态 import 的模块会识别不到。这个后面再提。
pyinstaller 默认会把可执行文件打到 dist 目录下,同时包含一个_internal
目录包含依赖等,这是--onedir
的行为。
使用--onefile
参数打包的话,则只会包含一个可执行文件。
打包成目录虽然分发稍显困难且大小更大(压缩后大小和单文件一致),但启动会更快,打包成单文件的话分发方便,但启动会更慢。
执行打包后的可执行文件时,工作目录将是可执行文件的所属目录。
这个可执行文件可以直接在界面上双击打开或者通过控制台执行,此时可以接受参数。
打包只会涉及到代码依赖。对于代码中引用的静态资源?见下。
关于 —onefile 和 —onedir
两者本质是一样的,只是前者将所有依赖也打到 exe 中,后者把依赖仍旧独立放置;前者每次执行时,都会把所有依赖都解压到一个临时文件夹中再执行,这会带来一些延时。
sys._MEIPASS
,该字段在执行打包后可执行文件时存在,表示依赖所处路径,这个路径对--onedir
来说总是 exe 本路径下的_internal
文件夹,对--onefile
来说每次都会是一个新的临时文件夹。
关于 spec
每次执行 pyinstaller 时,都会根据当前配置去生成 spec 文件,该文件表示编译配置,后续不修改配置,重新编译时,只需要直接传入该 spec 文件给 pyinstaller 即可:
1 |
|
FAQ
如何打包静态文件资源 Ver.1
这里分为两部分——标识静态文件资源,以及访问静态文件资源。
静态文件资源不会被识别出来,需要在编译时手动指定静态文件资源,同时指定它将要存放的位置,比如下面将项目中的asset/some.json
资源文件放置到资源文件夹(即上面的sys._MEIPASS
)下的asset
目录下:
1 |
|
然后访问,在代码中,不能使用相对路径去访问静态文件资源了,因为此时行为在直接执行和打包后执行时变得不同。需要定义一个函数去处理此差异:
1 |
|
DeepSeek 警告说,sys._MEIPASS
是 pyinstaller 的实现细节,依赖它是不优雅的。pyinstaller 同样重写了__file__
,顶层模块的__file__
将为_internal/xxx.pyc
,使用该__file__
也能够去获取资源文件。
如何导入动态加载的模块?
倘若我不加相关配置,下面的代码会报错:
1 |
|
因为 pyinstaller 没有识别到 requests 依赖。
使用--hidden-import
参数手动引入模块:
1 |
|
还有--collect-all
这样的参数,这里不表,真出了问题再查。
exe 启动时会打开一个控制台,如何修改此行为?
直接在文件管理器中打开打包后的 exe,会同时打开一个控制台。
使用--noconsole
参数避免此行为。
1 |
|
关于日志,没有控制台的话 print 就无处可去了,应当使用 logging 模块去打印日志,虽然我还没学过。
未捕获的异常我希望弹窗提醒或者日志!
这个和 pyinstaller 无关了,需重写异常处理的 hook,直接贴上 GPT 的建议(需要配置 logging):
1 |
|
如何更改使用的 python 的版本?
做不到,pyinstaller 只会使用安装 pyinstaller 时的那个 python 环境,要做到这个,只能使用指定版本的 python 创建虚拟环境然后再安装 pyinstaller,这样便能够安装该版本的 pyinstaller。
或者,使用全局环境的话,使用指定版本的 python 的-m
命令去执行 pyinstaller,如:
1 |
|
如何跨平台打包?
做不到,真想干建议上 docker 去进行编译。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 协议 ,转载请注明出处!