三种python打包方案踩坑总结
pyinstaller 打包问题¶
有的时候会出现打包失败的问题,这个可以参考pyinstaller使用注意事项
cx_freeze 打包问题¶
如果直接使用cxfreeze -c hello.py --target-dir dist的话,会发现cx_freeze把python标准库都给打包进去了,就比如这次打包一个tree模拟程序,虽然用7z压缩之后只有7M,但是解压之后竟然有21M,有些大,pyinstaller打包标准库也不过才6、7M,后来通过查找资料,了解到可以通过排除模块,因此写出了以下的命令cxfreeze -c hello.py --target-dir dist --exclude tkinter,email,http(后面还可以继续写用不到的模块,暂时先列出这几个),这样不断的调整,在不影响使用的前提下,打包之后的程序只有11M左右,压缩之后也只有4M,这个大小看着还行 还有,这次打包tree模拟程序时,发现在主机上打包会出现稀奇古怪的问题,哪怕是在虚拟环境中打包,也会出现一些问题,之后放在虚拟机里面打包,就没有这些问题了
nuitka打包问题¶
目前能通过打包的命令如下:
nuitka --standalone --mingw64 --plugin-enable=pyqt5 --windows-disable-console --output-dir=out --
nofollow-import-to=tkinter,numpy,pillow,PIL TAssistant.py
TAssistant时,会出现无法打包的问题,经过多次测试发现,虽然通过--include-module=pydoc,以为导入了pydoc,但是却并没有正确导入,所以目前的解决办法是在TAssistant.py中直接加入一个import pydoc,这样就强制导入pydoc模块了 其他可用的参数: --windows-icon-from-ico=resource/images/logo.png(导入图标)
--windows-company-name={AUTHOR}(软件信息,同下)
--windows-product-name={app_name}
--windows-product-version={VERSION}
--follow-import-to=common,components,view(如果有多个目录中含有python脚本,建议增加这一句命令)
Note
-
建议还是在虚拟环境中打包比较好,以免引入一些不需要的模块,导致程序体积增大
-
有的PyQt/PySide项目中含有类似
QThreadpoool、QSql等,如果想要使用Nuitka,需要将PyQt5迁移到PySide6,经过测试使用PySide6框架打包兼容性最好,但如果使用PyQt5、PyQt6、PySide2等,都会出现一些兼容问题导致无法正常运行 -
不同版本的nuitka对编译器要求不一样,考虑到编译器都比较大而且部署起来比较麻烦,所以如果没有特殊要求,建议还是固定一个版本,除非有重大更新再考虑更新nuitka
cython打包问题¶
为了解决HashChecker无法使用nuitka打包的问题,就采用cython+pyinstaller打包的方案,具体实现效果如下: 1. 写一个setup.py,内容如下:
from distutils.core import setup
from Cython.Build import cythonize
def generate_cython_file():
setup(
name='HashChecker',
ext_modules=cythonize(['config.py', 'logMonitor.py', 'main.py', 'task.py', './Widget/about_ui.py',
'./Widget/main_ui.py',
'./Widget/progress_ui.py',
'./Widget/settings_ui.py',
'./Widget/widget.py'], language_level=3),
)
generate_cython_file()
然后在终端中输入:
Note
cython打包有些怪,必须确保当前系统中安装VS C++(需要安装VS 2022/2019且需要安装C/C++组件),否则无法打包 还有入口文件和资源文件不要生成pyd,会很麻烦,还有因为其他的文件被打包成pyd,会导致pyinstaller找不到第三方模块,下面会提到
此时当前目录和build中都有一些pyd文件,这个时候同名的py文件就用不到了,因为我是在虚拟机打包的,多余的py文件就可以删掉了。
如果是第一次打包的话,也没有什么不一样的地方,直接pyinstaller file.py就行了,但需要进行多次打包,主要是会缺少第三方模块,这个时候需要根据pyinstaller的报错提示,手动引入第三方模块,这就需要修改spec文件了,修改的内容如下:
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['HashChecker.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=['markdown','PyQt5.QtWinExtras','openpyxl','chardet','Widget.about_ui','Widget.main_ui','Widget.progress_ui','Widget.settings_ui','Widget.widget','logMonitor','task','config','main'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='HashChecker',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='source\\main.ico',
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='HashChecker',
)
主要是hiddenimports这一项,需要将自己写的脚本以及第三方模块全部引入进去,之后再使用pyinstaller file.spec打包,就没有问题了,测试发现加载速度感觉快了一点,但是运行速度似乎没有明显的变化。
关于运行性能的对比¶
之前有人说nuitka打包后的程序速度媲美C++的,我也进行了一点测试,不完全准确,都使用for循环从1一直累加到10000000,然后采用不同的打包方式,每次运行后清空内存数据,对比结果如下(印象的数据,可能有误差):
| 打包方式 | 速度 |
|---|---|
| nuitka | 41秒 |
| python源码 | 60秒 |
| pyinstaller | 61秒 |
| cx_freeze | 68秒 |
总结¶
| 打包工具 | 优点 | 缺点 |
|---|---|---|
| pyinstaller | 1. 兼容性较好 2. 打包方便 | 1. 打包体积大 2. 容易被反编译(对安全没有较高要求可以忽略) |
| nuitka | 1. 打包体积较小 2. 有运行性能、内存占用的优化 | 1. 兼容性较差(只针对GUI开发) 2. 可能需要VC++环境 3. 打包比较麻烦,有一定的条件 |
| cx_freeze | 1. 兼容性较好 2. 打包方便 | 1. 打包体积大(可通过自行裁剪缩小体积) 2. 不太主流 |
因此,具体使用什么打包工具进行打包,需要根据实际需求来定,而不是盲目只使用其中一种打包方式
另外,cython更多的是作为一种编程语言来使用,利用cython生成pyd文件来加速只是它的次要作用,且经过测试对数据计算方面有比较大的明显,但对于I/O方面没有较大的优化