最直接可靠的方式是检查 __compiled__ 全局变量是否存在且为 True,Nuitka 编译时注入该变量,值恒为 True,所有目标平台均支持,且其他 Python 环境和打包工具(如 PyInstaller、cx_Freeze)均未定义。

判断 sys.executable 是否指向 Nuitka 生成的二进制文件
最直接可靠的方式是检查 sys.executable 路径是否为一个真实存在的、非 Python 解释器的可执行文件。Nuitka 编译后,sys.executable 指向最终打包出的二进制(如 myapp.exe 或 ./myapp),而非 python 或 python3。
注意:不能只靠文件名判断(比如含 “nuitka” 字样),因为用户可重命名;也不能依赖 sys.version 等字段,Nuitka 会模拟 CPython 版本字符串。
- 检查
os.path.isfile(sys.executable)且os.access(sys.executable, os.X_OK) - 用
shutil.which("python")或shutil.which("python3")对比 —— 若sys.executable不等于其中任一结果,大概率是 Nuitka 产物 - Windows 下可额外检查
sys.executable.endswith(".exe") and not sys.executable.lower().endswith(("python.exe", "py.exe"))
读取 Nuitka 运行时标记 __compiled__ 全局变量
Nuitka 在编译时会注入一个内置全局变量 __compiled__,值为 True。这是官方支持的检测方式,轻量且无副作用。
它在所有 Nuitka 编译目标(包括 Windows .exe、Linux/macOS 可执行文件、以及使用 --standalone 或 --onefile)中都存在,且未被任何常见 Python 环境(CPython、PyPy、conda、venv)设置过。
立即学习“Python免费学习笔记(深入)”;
- 直接写
if "__compiled__" in globals() and __compiled__:即可安全判断 - 不要用
try/except NameError包裹访问__compiled__—— 它是全局变量,不是内置函数,不存在调用开销 - 该变量在源码直跑时完全不存在,不会误判;也无需导入任何模块
避免误判:区分 PyInstaller、cx_Freeze 等其他打包工具
很多开发者会混淆 Nuitka 和 PyInstaller 的运行时特征。PyInstaller 设置的是 getattr(sys, 'frozen', False),而 cx_Freeze 用 sys.frozen == True。这些和 __compiled__ 互不干扰,但混用逻辑会导致错误。
- 不要把
sys.frozen当作 Nuitka 标志 —— Nuitka 默认不设它(除非显式加--enable-plugin=pyinstaller) - 不要检查
sys._MEIPASS—— 这是 PyInstaller 私有路径,Nuitka 不创建该属性 - 若需兼容多种打包器,应分别判断:
__compiled__(Nuitka)、getattr(sys, "frozen", False)(PyInstaller/cx_Freeze)、hasattr(sys, "_MEIPASS")(PyInstaller)
为什么不用 sys.argv[0] 或进程名检测?
sys.argv[0] 不可靠:它可能被脚本修改、可能只是相对路径、也可能在某些 one-file 启动场景下回退为解释器路径;进程名(如 psutil.Process().name())需要额外依赖,且在容器或受限环境里权限不足。
更关键的是,这些方式无法区分“Nuitka 编译后运行”和“用 Nuitka 编译的程序又被其他 launcher 调起”的情况 —— 而 __compiled__ 是编译期写死的 Python 层标识,语义明确。
真正容易被忽略的点是:如果你用了 Nuitka 的 --include-package 或插件机制动态加载模块,那些模块内部仍能访问到 __compiled__ —— 它是全局生效的,不是仅限于主模块。










