Windows 下 Nuitka 打包 Python 状态及路径研究

最近更新于 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
executable
sys.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 临时解压目录)
argv0
sys.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 状态及路径研究
Scroll to top