最可靠的方式是检查 sys.frozen 是否为 True;PyInstaller 打包后设为 True,未打包时为 None 或不存在,且需优先于其他模块导入和资源加载前执行检测。

判断 sys.frozen 是否为 True
PyInstaller 打包后,会在运行时把 sys.frozen 设为 True,这是最直接、最可靠的检测方式。未打包的普通 Python 解释器下该值为 None 或不存在。
注意:必须先导入 sys 模块,且不能依赖其他第三方库做判断(因为它们可能还没加载或路径未初始化)。
示例代码:
import sys
if getattr(sys, 'frozen', False):
print("正在以 PyInstaller 打包后的 exe 运行")
else:
print("正在以普通 Python 解释器运行")
检查 sys._MEIPASS 是否存在
打包后 PyInstaller 会解压资源到临时目录,并通过 sys._MEIPASS 暴露该路径。这个属性仅在 frozen 环境下存在,可作为辅助验证。
立即学习“Python免费学习笔记(深入)”;
但要注意:sys._MEIPASS 是私有属性(以下划线开头),不应在非检测场景中依赖它读取资源——应统一用 getattr(sys, '_MEIPASS', None) 安全访问,避免 AttributeError。
常见误用:直接写 sys._MEIPASS + '/data/config.json' 而不判空,会导致未打包时崩溃。
区分开发与打包环境的资源路径处理
很多脚本失败不是因为没检测出 exe,而是后续路径拼接出错。PyInstaller 下资源(如图片、配置文件)默认放在 sys._MEIPASS,而开发时通常相对当前脚本位置查找。
推荐写法:
import sys
import os
def resource_path(relative_path):
if getattr(sys, 'frozen', False):
base_path = sys._MEIPASS
else:
base_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base_path, relative_path)
config_file = resource_path('config.yaml')
这个函数能自动适配两种环境,避免硬编码路径或条件分支散落在各处。
为什么不能只靠 sys.executable 后缀判断
有人尝试用 sys.executable.endswith('.exe') 判断,这在 Windows 上看似可行,但极不可靠:
- Linux/macOS 下 PyInstaller 也能生成可执行文件(无 .exe 后缀)
- 某些 IDE(如 PyCharm)调试时也会启动带 .exe 的 Python 解释器(如
python.exe) - conda 环境或嵌入式 Python 可能也有 .exe,但并非 PyInstaller 打包产物
所以仅看文件扩展名会误判,sys.frozen 才是语义准确的标识。
sys.frozen 还没被检查,就会直接报错退出——这种失败不会提示“检测失败”,只会显示 FileNotFoundError 或类似异常。










