在使用Python开发项目时,如果项目依赖了如torch
这样的大型第三方库,打包后的体积可能会变得非常庞大(超过1GB)。传统的打包工具,如Nuitka或PyInstaller,可能会面临打包成功率低、耗时长、打包后体积巨大的问题。
为了解决这一问题,龙哥试了几种打包方法,各有好处。本文将探讨一种嵌入式打包的方法,即只分发解释器和核心代码,而第三方依赖则在用户第一次使用程序时现场安装。
解决思路
通过嵌入式打包,我们可以显著减少分发包的大小。用户在第一次运行程序时,程序会自动安装所需的第三方依赖。这种方法不仅减少了分发包的体积,也避免了传统打包方式可能出现的漏包问题。
示例代码
本文使用的软件版本为Python 3.10.9。如果你对嵌入式打包不太了解,可以先阅读相关的文章。以下是一个简单的示例项目结构和代码。
项目结构
app.py入口文件
from app.main import main
if __name__=="__main__":
main()
打包准备
- 准备项目依赖:在项目环境中运行
pip freeze > requirements.txt
来导出依赖文件。 - 准备嵌入式解释器:可以直接从python官方网站下载。
- 准备PyStand壳:从GitHub下载PyStand。
- 准备get-pip.py:用于下载pip工具,可以从bootstrap.pypa.io/get-pip.py获取。
开始打包
- 将代码复制到PyStand同级目录的
app
文件夹中,并将main.py
的内容复制到PyStand的pystand.int
文件中,文件重命名_pystand_static.int。 - 在
runtime
文件夹内新建download.py
文件,用于在首次运行时安装依赖。
import os
import sys
from pathlib import Path
print("第一次启动,缺少环境依赖,开始安装依赖,请保持网络畅通")
python_path = Path(sys.exec_prefix).joinpath("python.exe")
get_pip_script = Path(sys.exec_prefix).joinpath("get-pip.py")
command = f"{python_path} {get_pip_script}" # 安装pip工具
os.system(command)
requirements_path = Path(sys.exec_prefix).joinpath("requirements.txt") # 安装其他依赖
# 安装依赖,这是阿里源
command = str(
python_path) + " -m pip install --no-warn-script-location -i https://mirrors.aliyun.com/pypi/simple/ -r" + f"{requirements_path}"
status_code = os.system(command)
# 然后退出程序,提醒用户重启
if status_code != 0:
input("依赖安装失败")
else:
input("依赖安装完成,请重新打开程序,按回车键退出")
sys.exit()
- 修改
pystand.int
文件,捕获ModuleNotFoundError
错误,并调用runtime
文件夹中的Python解释器运行download.py
脚本。
import os
import sys
try:
from app.main import main
except ModuleNotFoundError:
from pathlib import Path
python_path = Path(sys.exec_prefix).joinpath("python.exe")
download_script = Path(sys.exec_prefix).joinpath("download.py")
command = f"{python_path} {download_script}"
os.system(command)
sys.exit()
if __name__=="__main__":
main()
- 将
requirements.txt
文件放置在runtime
文件夹中。 - 修改
runtime
文件夹中的._pth
文件,取消import site
前的注释。同时加入项目包目录../app - 删除PyStand同级目录的
sitepackage
文件夹。
最后目录如下。pystand.exe 可以任意修改名字
测试
- 双击PyStand,程序会提示缺少依赖并开始安装。
- 安装完成后,提示用户重启程序。
- 重新双击PyStand,程序将正常启动。
这样就完美实现小体积分发。打包难度也降低不少。听方便的,至于代码加密,可以用以下几种方法
- pyarmor直接加密项目包替换app目录
- nuitka 好像把py可以转成pyd,具体没操作
- 大家有什么更好的方法可以评论区放出来交流学习一下
最后运行,完美显示。随便写的一个游戏小工具。但是这样其实挺占空间。就这样吧,硬盘反正不值钱了
额外讲解
这种打包方式利用pip工具让用户在线安装依赖,分发体积小,避免了传统打包方式的缺点。特别适合依赖大型库的项目。用户无需关心本地Python环境,第三方库将被安装在runtime\Lib\site-packages
文件夹下。
对于依赖torch
等大型库的项目,可以将依赖文件拆分为两份,分别安装。这样可以确保torch
等库的正确安装。