PyInstaller 漏掉某些 import 是因其仅静态扫描 import 语句,无法识别动态导入如 __import__()、importlib.import_module()、字符串拼接模块名及运行时加载的子模块;需用 --hidden-import 手动补漏,但须写准实际模块名、多次重复参数;更可靠方式是编辑 .spec 文件的 hiddenimports 列表;复杂库需自定义钩子文件并通过 --additional-hooks-dir 指定。

PyInstaller 为什么漏掉某些 import?
它只静态扫描 import 语句,遇到 __import__()、importlib.import_module()、字符串拼接模块名、或运行时才决定加载的子模块(比如 cv2 的后端、PIL._imaging),就直接跳过。这不是 bug,是设计限制。
--hidden-import 不是万能解药,但得用对
手动补漏最常用,但要注意三点:
-
--hidden-import必须写**实际被导入的模块名**,不是包名。例如用torch时,常漏的是torch._C或torch.backends.cudnn,而不是只写torch - 多个模块要重复加参数,不能逗号分隔(旧版支持,新版已弃用):
pyinstaller --hidden-import=torch._C --hidden-import=torch.backends.cudnn main.py - 有些库的子模块名带点号,必须全写对,比如
sklearn.utils._testing,少一个下划线或大小写错误都不行
比 --hidden-import 更稳的方式:改 .spec 文件
生成一次 spec 后,直接编辑它,比反复敲长命令更可靠:
先生成:pyinstaller main.py → 得到 main.spec
再编辑该文件,在 Analysis 段里修改这两项:
hiddenimports=['torch._C', 'PIL._imaging', 'numpy.core._multiarray_umath'],
datas=[('path/to/your/data', 'data')],
然后用 pyinstaller main.spec 重打包。所有 hiddenimports 会进最终的 PYZ 归档,比命令行参数更彻底。
真正难搞的库,得靠钩子(hooks)
像 gevent、scrapy、自定义 C 扩展模块,光靠 --hidden-import 或 spec 不够。它们在运行时动态注册模块、修改 sys.modules,PyInstaller 默认钩子不覆盖。
这时要自己写钩子文件(hook-xxx.py),放在项目根目录或 hooks 子目录,内容类似:
from PyInstaller.utils.hooks import collect_all
datas, binaries, hiddenimports = collect_all('xxx')
再通过 --additional-hooks-dir=. 告诉 PyInstaller 加载它。没写对钩子,打包后大概率 ModuleNotFoundError 在启动瞬间抛出——这个错误不会出现在开发环境,只在 exe 运行时发生。
隐式导入最麻烦的地方在于:它不出现在代码里,也不报错在打包阶段,而是在用户双击 exe 后第一秒闪退。所以别等发给别人再验证,本地用干净虚拟环境 + 无 Python 环境的测试机跑一遍,才是真可靠。

















