将python文件打包成exe可执行文件的意义
我们首先要明白,将 Python 程序打包成可执行文件的重要意义:
- 便于分发和部署
跨平台兼容性:Python 程序通常需要在目标机器上安装 Python 解释器以及相关的库才能运行。打包后的可执行文件可以在不同的操作系统平台(如 Windows、Linux、Mac)上独立运行,无需用户手动安装 Python 环境。这大大简化了程序的分发过程,使得程序可以方便地提供给更多用户使用,而不用担心用户是否具备 Python 编程环境。
单一文件或目录交付:打包工具(如 Pyinstaller)可以将程序及其依赖整合到一个单独的可执行文件(使用-F或–onefile选项)或者一个包含可执行文件和依赖的目录(使用-D或–onedir选项)中。这种方式使得分发更加简洁,用户只需要获取这个打包后的文件或目录即可,而不需要处理复杂的项目结构和依赖关系。 - 保护源代码
隐藏代码逻辑:对于商业软件或需要保护知识产权的程序,打包后的可执行文件以二进制形式存在,使得源代码不容易被用户获取和查看。虽然这种方式不能完全防止逆向工程,但可以增加获取源代码的难度,保护开发者的算法、商业逻辑等重要信息。
加密选项(部分打包工具支持):一些高级的打包工具提供加密功能,如 Pyinstaller 的–key选项,可以对生成的可执行文件中的 Python 字节码进行加密,进一步增强源代码的安全性。 - 提升用户体验
简化运行流程:用户不需要了解 Python 或编程相关知识,也不需要进行复杂的环境配置。他们只需要双击可执行文件就可以启动程序,就像运行其他普通的应用程序一样,降低了用户使用程序的门槛。
图形化程序的独立运行:对于使用 Python 开发的图形用户界面(GUI)应用程序,打包成可执行文件可以使其独立运行,不需要在启动时显示命令行窗口(通过-w或–windowed选项),提供更专业、简洁的用户界面体验。 - 便于系统集成和部署
在受限环境中部署:在一些企业级或特定的系统环境中,可能对软件安装和运行有严格的限制。打包后的可执行文件可以作为一个独立的组件更容易地集成到这些系统中,无需担心与现有系统中的 Python 环境或其他软件产生冲突。
自动化部署:在大规模部署场景中,可执行文件可以更容易地被自动化部署工具识别和处理。例如,可以将打包后的程序与其他系统组件一起通过脚本进行批量部署,提高部署效率。
`
单python文件打包成exe
首先进入Anaconda Prompt
在选择的打包的环境下下载Pyinstaller库:
pip install Pyinstaller
如果不成功则使用管理员身份操作
下载成功后,cd/d进入打包文件所在的文件夹路径:
输入如下命令Pyinstaller -option1 -option2 -… 要打包的文件
关于-option几个常用选项我放到了文章末尾
我这里打包一个画跳动的爱心.py的文件,在这里使用三个参数:
Pyinstaller -F -w -i hhh.ico 画跳动的爱心.py
-F 生成单一可执行文件 -w 取消弹出命令窗口 -i 指定文件图标
回车后看到:
Successfully表明打包成功
成功后会在指定文件夹下生成三个文件:
分别是 dist,build 和以打包文件命名的.spec文件
而 dist文件中就是已经打包好的exe文件:
双击运行即可,结果如下:
该方法缺点很明显,打包的整个环境一定是会附带附带其它环境中无用的资源文件,而相关无用资源越多,exe文件大小自然就会越大,执行时间也会对应增加
因此,我的建议是可以单独创建一个新的环境,只下载py文件中需要引用的对应库。
多python文件打包成exe
在项目框架比较大的时候,一般会将项目拆分为很多分文件,最后在main文件中进行引用运行,此时,如果还是将main文件在环境中打包,则最终打包后会找不到主文件中引用的分文件,因为引用文件本来就不在环境中。此时就得跟换打包方式
这里我有个实例,下面讲述步骤
创建一个pack新环境以及整理好需要参与打包的所有文件,准备用来打包一个多文件框架的项目
准备文件如下:
这是一个人脸识别加区域内人数统计的项目
其中,Grab_bagDemo为主文件,剩下为资源文件和一些主文件需要引用的自定义py文件
进入Anaconda Prompt
输入conda activate 你的环境名 进入环境
然后cd/d 进入目标文件夹
运行如下命令:
pyi-makespec -option1 -option2 -… name.py
会得到:
则此步完成,主文件夹中会生成 .spec文件:
在spec文件中找到标记框对应语句,它实际上是一个列表,你需要将主文件,和主文件引用的自定义的Python文件写进这个列表里,可以是直接写文件名,或者是用路径(允许相对路径)都以字符串的形式写进这个列表里面,如果与name.py不在同一目录下,那就要写它的绝对路径,编辑完后保存文件。
之后回到命令框输入以下命令
Pyinstaller name.spec
等待打包完成即可
但是该方法资源文件还是要放在同一目录下才可以正确运行exe文件,而有时,为了安全性,有的项目的资源不方便外露,因此,需要用到下面的连带资源文件一起打包的方法
我的建议是,把资源文件(或者文件夹)都统一放到在一个与main文件同一目录下的文件夹里,方便打包,这里我创建了一个res文件夹,如图:
然后将.spec文件中的如图下标记框处修改成这样:
此处,由于现在把两个引用的UI文件也当成资源文件放进res中,所以第三行语句做了对应的修改,
此处还需要注意,main文件中的引用文件记得更改引用格式,因为那些自定义的文件路径改变了,比如我这里的UI文件,已经被放在了res文件夹下,所以要改成import res.
接着,网上有的博主还说需要修改文件的打开函数(但是我后面会提到,我的代码中也使用了很多open函数,可是修改了反而会报错)网上有很多种修改方法,我使用的方法如下:
引入特定的模块:
这个模块代码很简单,放在下面一定要将模块命名为_.py,并在引用其它第三方模块之前就引用它,但又一定要在下划线开头的模块之后引用:
# _.py
import builtins
def wrapper(function):
def _open(*args, **kw):
""" 修改路径 """
_args = list(args)
_args[0] = __file__[:-4] + args[0]
if kw.get('file'):
kw['file'] = __file__[:-4] + kw['file']
return function(*_args, **kw)
return _open
setattr(builtins, 'open', wrapper(open))
然后要记得在.spec文件中加上_.py文件
最后保存文件
返回命令框开始打包
Pyinstaller name.spec
等待打包完成即可
这里有点小插曲,因为能力不足而无法解释说明,我翻阅了大量多python文件打包exe的资料,基本上的难点都在对open函数的修改,上述我的方法正常流程来讲应该修改完了open函数再打包就可以实现了,但是奇怪的是我是用了修改open函数的方法,但是最后文件执行时需要使用open函数的地方虽然没有报错,但却没有发挥其原本功效,我调换了_.py的引用位置,没有用,然后试着直接把_.py文件的代码加到每一个需要使用open函数的文件中,还是没有用,最后没办法了,直接省去了修改open函数这一步,接果程序可以运行了,原因还不知到,后续有时间找到了补上
最后展示结果:
这个UI界面便是exe文件运行后的结果,所有功能都没有报错,与运行源文件的效果一摸一样,在exe的上面三个文件是主文件运行时生成,是在代码中定义生成到运行文件所在文件夹的,所以不用担心主文件后缀改变而导致运行时会生成的额外文件丢失问题,这个在Pyinstaller库中应该直接帮忙解决了,而这是本代码会生成的三个额外文件,所以实际上dist中只有name.exe文件,而且是独立的,不会暴露任何私资源的可执行文件。
由于现在是急需给甲方这个项目的可执行文件,只是匆匆临时学了个大概,所以打包的底层逻辑与一些更具体的细节还没系统的学习,只是我的这个项目能正确打包了而已,日后时间充足了我会做好进一步的补充
补充资料
一.
分享一下转文件后缀的两个网站,我的项目图标就到第一个网站里做的
aconvert.com
cloudconvert.com
具体怎么操作的就不说明了,挺简单的
二.
以下是Pyinstaller的常用选项:
- 输出相关选项
-F, --onefile:
作用:将所有的 Python 解释器、脚本、依赖库等打包成一个独立的可执行文件。
示例:pyinstaller -F main.py
-D, --onedir:
作用:创建一个包含可执行文件和依赖文件的目录,而不是一个单独的可执行文件。这样在运行程序时,可执行文件可以从这个目录中找到所需的库和资源。
示例:pyinstaller -D main.py
-n , --name :
作用:指定生成的输出文件或目录的名称(不包括扩展名)。如果不指定,默认使用脚本的主文件名。
示例:pyinstaller -n my_program main.py
- 窗口相关选项
-c, --console:
作用:在打包后的程序启动时打开一个控制台窗口(对于基于控制台的应用程序)。这是默认行为,如果你的脚本是命令行工具类型的应用,通常会有这个控制台窗口显示程序的输出和接收输入。
示例:pyinstaller -c main.py
-w, --windowed:
作用:对于有图形用户界面(GUI)的程序,使用这个选项可以在运行时不显示控制台窗口。适用于使用Tkinter、PyQt等创建的 GUI 应用。
示例:pyinstaller -w main.py
- 图标相关选项
-i <icon_path>, --icon <icon_path>:
作用:指定生成的可执行文件的图标文件路径。图标文件格式通常为.ico(Windows)等。
示例:pyinstaller -i my_icon.ico main.py
- 分析和排除选项
–analysis <analysis_mode>:
作用:指定分析模式,用于控制Pyinstaller如何分析脚本的依赖关系。不同的模式适用于不同的情况,比如更深入的分析或者更快的分析等,但这个选项相对高级,一般用户较少使用。
–exclude-module <module_name>:
作用:指定在打包过程中要排除的模块。如果你的脚本中某些模块你不想包含在最终的可执行文件中(可能因为你知道它们在目标环境中已经存在或者不需要),可以使用这个选项。
示例:pyinstaller --exclude-module tkinter main.py
- 数据文件处理选项
–add-data <src;dst>:
作用:添加额外的数据文件或目录到打包后的文件或目录中。src是源数据文件或目录的路径,dst是在打包后的目标路径(在可执行文件或目录中的相对路径)。
示例:pyinstaller --add-data “config.ini;.” main.py(将config.ini文件添加到打包后的同一目录下)
–add-binary <src;dst>:
作用:类似于–add-data,但主要用于添加二进制文件。
示例:pyinstaller --add-binary “my_lib.dll;lib”(将my_lib.dll文件添加到打包后的lib目录下)
- 其他杂项选项
-v, --version:
作用:显示Pyinstaller的版本信息。
–clean:
作用:在打包之前清理之前的构建目录和临时文件,以确保每次打包都是基于干净的环境。
示例:pyinstaller --clean main.py
–debug <debug_mode>:
作用:启用调试模式,用于在打包过程中获取更多的调试信息,帮助解决打包过程中遇到的问题。不同的调试模式可以指定不同级别的详细信息输出。
示例:pyinstaller --debug all main.py(输出所有级别的调试信息)
–runtime-hook <hook_script.py>:
作用:指定一个运行时的钩子脚本。这个脚本可以在程序启动时执行一些额外的操作,比如修改环境变量、加载特定的配置等。
示例:pyinstaller --runtime - hook my_hook.py main.py
–collect-submodules <package_name>:
作用:收集指定包下的所有子模块。当Pyinstaller不能自动检测到某些模块的依赖关系时,可以使用这个选项来确保相关的子模块都被包含。
示例:pyinstaller --collect - submodules my_package main.py
–collect-data <package_name>:
作用:收集指定包相关的数据文件。这有助于确保与某个包相关的非 Python 文件(如配置文件、模板文件等)都被正确打包。
示例:pyinstaller --collect - data my_package main.py
–hidden-import <module_name>:
作用:指定一个Pyinstaller可能无法自动检测到的隐藏导入模块。当程序运行时需要某个模块,但Pyinstaller在分析过程中没有发现这个依赖关系时,可以使用这个选项手动添加。
示例:pyinstaller --hidden - import some_hidden_module main.py
–upx-dir <upx_path>:
作用:指定UPX(一个可执行文件压缩工具)的路径。如果安装了UPX,Pyinstaller可以使用它来压缩生成的可执行文件,减小文件大小。
示例:pyinstaller --upx - dir /usr/local/bin/upx main.py
–key <encryption_key>:
作用:使用指定的密钥对生成的可执行文件中的 Python 字节码进行加密。这可以在一定程度上保护代码的安全性。
示例:pyinstaller --key my_secret_key main.py
–splash <splash_image_path>:
作用:为打包后的程序添加启动画面。splash_image_path是启动画面图像的路径。启动画面可以提高程序启动时的视觉效果。
示例:pyinstaller --splash splash.png main.py
–distpath <distribution_path>:
作用:指定生成的可执行文件或目录的输出路径。默认情况下,输出到dist目录。
示例:pyinstaller --distpath /my/custom/dist/dir main.py
–workpath <work_path>:
作用:指定Pyinstaller在打包过程中的工作目录路径。默认情况下,会在临时目录中创建工作目录。
示例:pyinstaller --workpath /my/custom/work/dir main.py
–specpath <spec_path>:
作用:指定生成的.spec文件的存储路径。.spec文件是Pyinstaller用于控制打包过程的配置文件。默认情况下,与脚本文件在同一目录。
示例:pyinstaller --specpath /my/custom/spec/dir main.py
–strip:
作用:对生成的可执行文件进行 strip 操作(去除符号信息等),可以减小文件大小,但可能会影响调试功能。
–noupx:
作用:禁止使用UPX对可执行文件进行压缩,即使UPX已安装并且配置了路径。
–log-level :
作用:设置Pyinstaller在打包过程中的日志级别。常见的级别有DEBUG、INFO、WARN、ERROR等,用于控制在控制台输出的信息量。
示例:pyinstaller --log - level INFO main.py
–version-file <version_file_path>:
作用:指定一个包含版本信息的文件路径。这个文件中的信息可以被打包到可执行文件中,用于显示程序的版本号等相关信息。
示例:pyinstaller --version - file version_info.txt main.py
–ascii:
作用:强制Pyinstaller使用 ASCII 编码处理所有文件和字符串。这可能在某些特殊环境下有用,但可能会导致对非 ASCII 字符的处理问题。
–disable-windowed-traceback:
作用:在窗口模式(-w选项)下,禁用显示详细的错误回溯信息。这样在程序崩溃时,不会显示一个包含详细错误信息的窗口,可能更适合最终发布的版本。
–noconfirm:
作用:在打包过程中,对于一些需要用户确认的操作(如覆盖现有文件等),自动执行而不提示用户确认。
最常用的其实就是 -F -w -i -n,剩下的都可以根据自己对于文件的要求来自行添加
这只是Pyinstaller选项的一个较为全面的列表,不同版本可能会有一些细微的差别,具体使用时可以参考:
Pyinstaller的官方文档