最近更新于 2026-09-05 23:39
测试环境
- Windows 11 25H2
- Python 3.14.5
- Nuitka 4.2
- PowerShell 7.6.5
研究
主要测试内容的简单源码
import sys
isCompiled = '__compiled__' in globals()
print(f'是否打包: {isCompiled}')
print(f'sys.exeutable: {sys.executable}')
print(f'sys.argv[0]: {sys.argv[0]}')
print(f'__file__: {__file__}')
compiledObj = globals().get('__compiled__')
if compiledObj is not None:
print(f'onefile: {compiledObj.onefile}')
print(f'original_argv0: {compiledObj.original_argv0}')
print(f'containing_dir: {compiledObj.containing_dir}')
input('按回车键继续...')
完整测试情况的一键 PowerShell 脚本
# ==========================================
# Nuitka 自动化测试与结果验证脚本
# ==========================================
$ErrorActionPreference = "Stop"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
Set-Location $ScriptDir
# 1. 初始化虚拟环境与依赖
if (-not (Test-Path "venv")) {
Write-Host "Creating Virtual Environment..." -ForegroundColor Green
python -m venv venv
}
& .\venv\Scripts\Activate.ps1
python -m pip install --upgrade pip | Out-Null
pip install nuitka==4.2 | Out-Null
# 2. 准备测试项目源码与目录结构
Write-Host "Preparing test source files..." -ForegroundColor Green
New-Item -ItemType Directory -Force -Path "testmodule" | Out-Null
# 【主程序模式】核心代码 (main.py)
@'
import sys
isCompiled = '__compiled__' in globals()
executable = sys.executable
argv0 = sys.argv[0]
file = __file__
compiledObj = globals().get('__compiled__')
onefile = None
original_argv0 = None
containing_dir = None
if compiledObj is not None:
onefile = compiledObj.onefile
original_argv0 = compiledObj.original_argv0
containing_dir = compiledObj.containing_dir
print(f'是否打包: {isCompiled}')
print(f'executable: {executable}')
print(f'argv0: {argv0}')
print(f'__file__: {file}')
print(f'onefile: {onefile}')
print(f'original_argv0: {original_argv0}')
print(f'containing_dir: {containing_dir}')
'@ | Set-Content -Path "main.py" -Encoding UTF8
# 【模块模式】模块代码 (testmodule/testmodule.py)
@'
import sys
isCompiled = '__compiled__' in globals()
executable = sys.executable
argv0 = sys.argv[0]
file = __file__
compiledObj = globals().get('__compiled__')
onefile = None
original_argv0 = None
containing_dir = None
if compiledObj is not None:
onefile = compiledObj.onefile
original_argv0 = compiledObj.original_argv0
containing_dir = compiledObj.containing_dir
'@ | Set-Content -Path "testmodule\testmodule.py" -Encoding UTF8
# 【模块模式】主入口代码 (mainmodule.py)
@'
from testmodule import testmodule
print(f'是否打包: {testmodule.isCompiled}')
print(f'executable: {testmodule.executable}')
print(f'argv0: {testmodule.argv0}')
print(f'__file__: {testmodule.file}')
print(f'onefile: {testmodule.onefile}')
print(f'original_argv0: {testmodule.original_argv0}')
print(f'containing_dir: {testmodule.containing_dir}')
'@ | Set-Content -Path "mainmodule.py" -Encoding UTF8
# 3. Nuitka 打包构建(共 4 个 Target)
Write-Host "`n==========================================" -ForegroundColor Cyan
Write-Host " Building Executables with Nuitka..." -ForegroundColor Cyan
Write-Host "==========================================" -ForegroundColor Cyan
# 正确的逻辑:多文件打包使用 --standalone;单文件打包同时使用 --standalone 和 --onefile
$BuildTasks = @(
@{ Entry = "main.py"; OutputDir = "dist_main_standalone"; Flags = @("--standalone"); Name = "main.exe" },
@{ Entry = "main.py"; OutputDir = "dist_main_onefile"; Flags = @("--standalone", "--onefile"); Name = "mainonefile.exe" },
@{ Entry = "mainmodule.py"; OutputDir = "dist_mod_standalone"; Flags = @("--standalone"); Name = "mainmodule.exe" },
@{ Entry = "mainmodule.py"; OutputDir = "dist_mod_onefile"; Flags = @("--standalone", "--onefile"); Name = "mainmoduleonefile.exe" }
)
foreach ($Task in $BuildTasks) {
$entryFile = $Task["Entry"]
$outDir = $Task["OutputDir"]
$flags = $Task["Flags"]
$exeName = $Task["Name"]
$flagsStr = $flags -join " "
Write-Host "Building $exeName [$flagsStr]..." -ForegroundColor Yellow
$StartTime = Get-Date
nuitka `
$flags `
--follow-imports `
--assume-yes-for-downloads `
"--output-dir=$outDir" `
"--output-filename=$exeName" `
$entryFile | Out-Null
$Elapsed = (New-TimeSpan -Start $StartTime -End (Get-Date)).TotalSeconds
Write-Host "Built $exeName in [ $(([math]::Round($Elapsed, 2)))s ]" -ForegroundColor DarkGreen
}
# 4. 执行测试与结果输出
Write-Host "`n==========================================" -ForegroundColor Cyan
Write-Host " Executing Test Cases & Gathering Results " -ForegroundColor Cyan
Write-Host "==========================================" -ForegroundColor Cyan
function Run-TestAndPrint {
param(
[string]$Title,
[string]$SearchDir,
[string]$ExeName,
[string]$DirectCommand = $null,
[string[]]$ArgsList = @()
)
Write-Host "`n------------------------------------------" -ForegroundColor White
Write-Host " Case: $Title" -ForegroundColor Yellow
Write-Host "------------------------------------------" -ForegroundColor White
if ($DirectCommand) {
& $DirectCommand $ArgsList
} else {
# 在输出目录下寻找生成的 EXE
$TargetFile = Get-ChildItem -Path $SearchDir -Filter $ExeName -Recurse -File -ErrorAction SilentlyContinue | Select-Object -First 1
if ($TargetFile) {
& $TargetFile.FullName $ArgsList
} else {
Write-Host "错误: 未在目录 $SearchDir 下找到生成的可执行文件 $ExeName" -ForegroundColor Red
}
}
}
# 场景 1:主程序 - 源码执行
Run-TestAndPrint -Title "主程序 - 源码执行" -DirectCommand ".\venv\Scripts\python.exe" -ArgsList @(".\main.py")
# 场景 2:主程序 - 多文件打包执行 (--standalone)
Run-TestAndPrint -Title "主程序 - 多文件打包执行 (--standalone)" -SearchDir "dist_main_standalone" -ExeName "main.exe"
# 场景 3:主程序 - 单文件打包执行 (--standalone --onefile)
Run-TestAndPrint -Title "主程序 - 单文件打包执行 (--standalone --onefile)" -SearchDir "dist_main_onefile" -ExeName "mainonefile.exe"
# 场景 4:模块 - 源码执行
Run-TestAndPrint -Title "模块 - 源码执行" -DirectCommand ".\venv\Scripts\python.exe" -ArgsList @(".\mainmodule.py")
# 场景 5:模块 - 多文件打包执行 (--standalone)
Run-TestAndPrint -Title "模块 - 多文件打包执行 (--standalone)" -SearchDir "dist_mod_standalone" -ExeName "mainmodule.exe"
# 场景 6:模块 - 单文件打包执行 (--standalone --onefile)
Run-TestAndPrint -Title "模块 - 单文件打包执行 (--standalone --onefile)" -SearchDir "dist_mod_onefile" -ExeName "mainmoduleonefile.exe"
Write-Host "`n==========================================" -ForegroundColor Green
Write-Host " All 6 Test Cases Completed Successfully!" -ForegroundColor Green
Write-Host "==========================================" -ForegroundColor Green
测试输出结果
------------------------------------------
Case: 主程序 - 源码执行
------------------------------------------
是否打包: False
executable: C:\work\development\nuitka\venv\Scripts\python.exe
argv0: .\main.py
__file__: C:\work\development\nuitka\main.py
onefile: None
original_argv0: None
containing_dir: None
------------------------------------------
Case: 主程序 - 多文件打包执行 (--standalone)
------------------------------------------
是否打包: True
executable: C:\work\DEVELO~1\nuitka\DIST_M~1\MAIN~1.DIS\python.exe
argv0: C:\work\development\nuitka\dist_main_standalone\main.dist\main.exe
__file__: C:\work\DEVELO~1\nuitka\DIST_M~1\MAIN~1.DIS\main.py
onefile: False
original_argv0: C:\work\development\nuitka\dist_main_standalone\main.dist\main.exe
containing_dir: C:\work\DEVELO~1\nuitka\DIST_M~1
------------------------------------------
Case: 主程序 - 单文件打包执行 (--standalone --onefile)
------------------------------------------
是否打包: True
executable: C:\Users\ZHAOHO~1\AppData\Local\Temp\onefile_36000_032854_Eto8l3VYNbU\python.exe
argv0: C:\work\development\nuitka\dist_main_onefile\mainonefile.exe
__file__: C:\Users\ZHAOHO~1\AppData\Local\Temp\onefile_36000_032854_Eto8l3VYNbU\main.py
onefile: True
original_argv0: C:\work\development\nuitka\dist_main_onefile\mainonefile.exe
containing_dir: C:\work\development\nuitka\dist_main_onefile
------------------------------------------
Case: 模块 - 源码执行
------------------------------------------
是否打包: False
executable: C:\work\development\nuitka\venv\Scripts\python.exe
argv0: .\mainmodule.py
__file__: C:\work\development\nuitka\testmodule\testmodule.py
onefile: None
original_argv0: None
containing_dir: None
------------------------------------------
Case: 模块 - 多文件打包执行 (--standalone)
------------------------------------------
是否打包: True
executable: C:\work\DEVELO~1\nuitka\DIST_M~3\MAINMO~1.DIS\python.exe
argv0: C:\work\development\nuitka\dist_mod_standalone\mainmodule.dist\mainmodule.exe
__file__: C:\work\DEVELO~1\nuitka\DIST_M~3\MAINMO~1.DIS\testmodule\testmodule.py
onefile: False
original_argv0: C:\work\development\nuitka\dist_mod_standalone\mainmodule.dist\mainmodule.exe
containing_dir: C:\work\DEVELO~1\nuitka\DIST_M~3
------------------------------------------
Case: 模块 - 单文件打包执行 (--standalone --onefile)
------------------------------------------
是否打包: True
executable: C:\Users\ZHAOHO~1\AppData\Local\Temp\onefile_6060_434065_GGL3bKO_4x8\python.exe
argv0: C:\work\development\nuitka\dist_mod_onefile\mainmoduleonefile.exe
__file__: C:\Users\ZHAOHO~1\AppData\Local\Temp\onefile_6060_434065_GGL3bKO_4x8\testmodule\testmodule.py
onefile: True
original_argv0: C:\work\development\nuitka\dist_mod_onefile\mainmoduleonefile.exe
containing_dir: C:\work\development\nuitka\dist_mod_onefile
结论
| 属性 / 变量 | 1. 主程序 – 源码 | 2. 主程序 – 多文件 (--standalone) |
3. 主程序 – 单文件 (--standalone --onefile) |
4. 模块 – 源码 | 5. 模块 – 多文件 (--standalone) |
6. 模块 – 单文件 (--standalone --onefile) |
|---|---|---|---|---|---|---|
是否打包'__compiled__' in globals() |
False |
True |
True |
False |
True |
True |
executablesys.executable |
...\venv\Scripts\python.exe(虚拟环境 Python) |
...\dist_main_standalone\main.dist\python.exe(分发目录内建 Python) |
...\AppData\Local\Temp\onefile_36000_...\python.exe(Temp 临时解压目录) |
...\venv\Scripts\python.exe(虚拟环境 Python) |
...\dist_mod_standalone\mainmodule.dist\python.exe(分发目录内建 Python) |
...\AppData\Local\Temp\onefile_6060_...\python.exe(Temp 临时解压目录) |
argv0sys.argv[0] |
.\main.py(命令行相对路径) |
...\dist_main_standalone\main.dist\main.exe(EXE 绝对路径) |
...\dist_main_onefile\mainonefile.exe(EXE 绝对路径) |
.\mainmodule.py(命令行相对路径) |
...\dist_mod_standalone\mainmodule.dist\mainmodule.exe(EXE 绝对路径) |
...\dist_mod_onefile\mainmoduleonefile.exe(EXE 绝对路径) |
__file__ |
...\nuitka\main.py(主程序源码路径) |
...\dist_main_standalone\main.dist\main.py(分发目录映射路径) |
...\AppData\Local\Temp\onefile_36000_...\main.py(Temp 临时解压路径) |
...\nuitka\testmodule\testmodule.py(被调模块源码路径) |
...\dist_mod_standalone\mainmodule.dist\testmodule\testmodule.py(分发目录模块映射路径) |
...\AppData\Local\Temp\onefile_6060_...\testmodule\testmodule.py(Temp 临时解压模块路径) |
onefile__compiled__.onefile |
None |
False |
True |
None |
False |
True |
original_argv0__compiled__.original_argv0 |
None |
...\dist_main_standalone\main.dist\main.exe(调用 EXE 绝对路径) |
...\dist_main_onefile\mainonefile.exe(调用 EXE 绝对路径) |
None |
...\dist_mod_standalone\mainmodule.dist\mainmodule.exe(调用 EXE 绝对路径) |
...\dist_mod_onefile\mainmoduleonefile.exe(调用 EXE 绝对路径) |
containing_dir__compiled__.containing_dir |
None |
...\dist_main_standalone\main.dist(分发文件夹根目录) |
...\dist_main_onefile(外部 EXE 所在目录) |
None |
...\dist_mod_standalone\mainmodule.dist(分发文件夹根目录) |
...\dist_mod_onefile(外部 EXE 所在目录) |
应用
打包状态与路径
注意应用中 Nuitka 是使用了 –standalone 参数的情景下(独立模式与源码脱钩,且附带打包完整依赖),是否使用这个参数,__file__ 的值是有区别的。
示例源码:test.py
import os
import sys
isCompiled = '__compiled__' in globals()
print(f'是否已打包:{isCompiled}')
programFilePath = os.path.abspath(sys.argv[0])
print(f'程序文件绝对路径(方法一):{programFilePath}')
filePath = __file__
print(f'当前文件绝对路径(调用者源码文件或调用者源码文件打包后映射路径):{filePath}')
appDir = os.path.dirname(__file__)
print(f'程序运行时可访问打包资源的根目录:{appDir}')
compiledObj = globals().get('__compiled__')
if compiledObj is not None:
programFilePath = compiledObj.original_argv0
print(f'程序文件绝对路径(方法二):{programFilePath}')
programFileDir = compiledObj.containing_dir
print(f'程序文件所在目录:{programFileDir}')
onefile = compiledObj.onefile
print(f'是否为单文件打包:{onefile}')
else: # 未打包
pythonPath = sys.executable
print(f'Python 解释器路径:{pythonPath}')
executableCommandString = sys.executable + ' ' + os.path.abspath(sys.argv[0])
print(f'可执行命令字符串:{executableCommandString}')
源码运行结果
是否已打包:False
程序文件绝对路径(方法一):C:\work\development\nuitka\test.py
当前文件绝对路径(调用者源码文件或调用者源码文件打包后映射路径):C:\work\development\nuitka\test.py
程序运行时可访问打包资源的根目录:C:\work\development\nuitka
Python 解释器路径:C:\work\development\nuitka\venv\Scripts\python.exe
可执行命令字符串:C:\work\development\nuitka\venv\Scripts\python.exe C:\work\development\nuitka\test.py
单文件打包运行
$startTime = Get-Date
python -m venv venv
.\venv\Scripts\Activate.ps1
python.exe -m pip install --upgrade pip
pip install nuitka==4.2
nuitka `
--remove-output `
--standalone `
--onefile `
--output-dir=dist `
--output-filename=test `
.\test.py
$endTime = Get-Date
$elapsedTime = New-TimeSpan -Start $startTime -End $endTime
Write-Output "程序构建用时:$($elapsedTime.TotalSeconds) 秒"
.\dist\test.exe
单文件打包运行结果
是否已打包:True
程序文件绝对路径(方法一):C:\work\development\nuitka\dist\test.exe
当前文件绝对路径(调用者源码文件或调用者源码文件打包后映射路径):C:\Users\ZHAOHO~1\AppData\Local\Temp\onefile_37876_228418_z8xZM769z-4\test.py
程序运行时可访问打包资源的根目录:C:\Users\ZHAOHO~1\AppData\Local\Temp\onefile_37876_228418_z8xZM769z-4
程序文件绝对路径(方法二):C:\work\development\nuitka\dist\test.exe
程序文件所在目录:C:\work\development\nuitka\dist
是否为单文件打包:True
多文件打包运行
$startTime = Get-Date
python -m venv venv
.\venv\Scripts\Activate.ps1
python.exe -m pip install --upgrade pip
pip install nuitka==4.2
nuitka `
--remove-output `
--standalone `
--output-dir=dist `
--output-filename=test `
.\test.py
$endTime = Get-Date
$elapsedTime = New-TimeSpan -Start $startTime -End $endTime
Write-Output "程序构建用时:$($elapsedTime.TotalSeconds) 秒"
.\dist\test.dist\test.exe
多文件打包运行结果
是否已打包:True
程序文件绝对路径(方法一):C:\work\development\nuitka\dist\test.dist\test.exe
当前文件绝对路径(调用者源码文件或调用者源码文件打包后映射路径):C:\work\DEVELO~1\nuitka\dist\TEST~1.DIS\test.py
程序运行时可访问打包资源的根目录:C:\work\DEVELO~1\nuitka\dist\TEST~1.DIS
程序文件绝对路径(方法二):C:\work\development\nuitka\dist\test.dist\test.exe
程序文件所在目录:C:\work\DEVELO~1\nuitka\dist
是否为单文件打包:False
短路径转长路径
上面测试中可以发现部分路径存在 ~ 符号,这个是 Windows 的 8.3 短路径,名称显示为 前6位大写字母~数字,如果要转换为完整路径可以参考下面示例。
test.py
from pathlib import Path
def toFullPath(filename: str) -> str:
return str(Path(filename).resolve())
print(f'可能存在短路径:{__file__}')
fullFilePath = toFullPath(__file__)
print(f'完整路径:{fullFilePath}')
可能存在短路径:C:\work\DEVELO~1\nuitka\dist\TEST~1.DIS\test.py
完整路径:C:\work\development\nuitka\dist\test.dist\test.py
Windows 下 Nuitka 打包 Python 状态及路径研究
