Administrator
发布于 2026-01-06 / 2 阅读
0
0

32. Python项目打包常用命令

不改代码,只用打包命令,程序打包后虽然能运行,但一旦读文件(比如读配置、读模型)就会因为找不到路径而报错。

下面我为你准备了 “全流程速查表”“路径函数修改指南”


第一部分:Python 项目全生命周期命令速查表 (基于 uv + Windows)

请保存这张表,它涵盖了从“出生”到“出厂”的所有关键动作。

阶段

动作

核心命令

注解 (小白话翻译)

1. 新建

初始化项目

uv init my_project


cd my_project

造房子。建文件夹,生成 pyproject.toml (户口本)。

2. 开发

添加依赖库

uv add requests

买家具。自动安装 requests 并记入户口本。

添加开发工具

uv add --dev pyinstaller

买装修工具。只在开发打包时用,不带给用户。

3. 运行

运行代码

uv run main.py

试住。全自动创建环境、同步依赖并运行。

4. 维护

切换Python版本

uv python pin 3.10

换地基。指定必须用 Python 3.10。

同步环境

uv sync

照图施工。别人发给你的项目,用这就对了。

5. 打包

打包成软件

uv run pyinstaller -F main.py

自热火锅。生成单文件 EXE。

(带资源打包)

uv run pyinstaller -F main.py --add-data "config.txt;."

带调料包。把外部文件也塞进 EXE 里。


第二部分:关于“路径读取函数”的深度解析

你必须在源代码中加入这个函数,因为 “开发环境”“打包后的环境”,文件的物理位置变了。

1. 为什么要改代码?

  • 在开发时config.txt 就在你手边的文件夹里。

  • 打包后:PyInstaller 会把所有文件压缩到 .exe 肚子里。当你双击运行 .exe 时,它会偷偷把文件解压到电脑的一个 临时文件夹 (通常在 C:\Users\AppData\Local\Temp\_MEIxxxx)。

  • 问题:如果你代码里写死读取 当前目录/config.txt,它会去 .exe 所在的目录找,而不是去那个 临时文件夹 找,结果就是 File Not Found

2. 如何修改? (Copy-Paste 级教程)

请把下面的代码复制到你的 main.py 最上方。

Python

import sys
import os

# --- 核心函数:解决打包后的路径问题 ---
def get_resource_path(relative_path):
    """
    获取资源的绝对路径。
    1. 如果是开发环境,它返回当前目录下的路径。
    2. 如果是打包后的 exe,它返回解压后的临时目录路径。
    """
    if hasattr(sys, '_MEIPASS'):
        # PyInstaller 打包后的临时目录
        base_path = sys._MEIPASS
    else:
        # 开发时的当前目录
        base_path = os.path.abspath(".")
    
    return os.path.join(base_path, relative_path)
# ------------------------------------

# --- 你的业务代码 ---

# ❌ 以前你是这样写的 (打包后会报错):
# with open("config.txt", "r", encoding='utf-8') as f:
#     content = f.read()

# ✅ 现在你要这样写 (开发和打包都能跑):
config_file_path = get_resource_path("config.txt") 
print(f"正在读取文件,路径是: {config_file_path}") # 打印出来看看,你会发现路径变了

try:
    with open(config_file_path, "r", encoding='utf-8') as f:
        content = f.read()
        print("读取成功内容:", content)
except FileNotFoundError:
    print("错误:找不到文件!")

input("按回车键退出...") # 防止黑窗口一闪而过

3. 这里的关键配合:命令也要改!

代码改好了,只是告诉程序 “去哪里找”。

你还需要在打包命令里告诉打包工具 “把谁带上”。

如果你的项目里有一个 config.txt 需要打包,命令必须加上 --add-data

PowerShell

# 格式: --add-data "源文件名;目标路径"
# 注意: Windows 中间用分号 ; 隔开,目标路径通常写 . (表示根目录)

uv run pyinstaller -F main.py --add-data "config.txt;."

总结你的“防坑”三部曲

  1. 改代码:把所有 open("xxx") 里的路径,都套上 get_resource_path("xxx")

  2. 加参数:运行打包命令时,记得用 --add-data 把文件带上。

  3. 看结果:双击生成的 exe,如果它能顺利读出文件,说明你成功了。

你可以现在就在你的 demo_test 文件夹里,新建一个 config.txt (里面随便写几个字),然后用上面的代码和命令试一次。这是理解“打包原理”最好的实验。--


这是一个非常好的问题!这里的概念确实容易混淆,因为中文里我们有时候都叫“装Python”。

但实际上,你在 Cursor 里做的这两步操作,和 uv 做的事情完全属于两个不同的维度

我们要区分三个概念:“发动机”、“仪表盘”和“钥匙”


1. UV 帮你装的是“发动机” (The Engine)

  • 动作uv init, uv sync

  • 产物uv 在你的硬盘里(.venv 文件夹)悄悄下载并安装了一个 Python 解释器(Executable)

  • 作用:它负责真正地跑代码。没有它,代码就是一堆废纸。

  • 状态:它现在静静地躺在你的 D:\demo\.venv\Scripts\python.exe 里。

2. Cursor 让你装的是“仪表盘” (The Extension)

  • 你的疑问:“为什么在 Cursor 里面还要装 Python?”

  • 纠正:你在 Cursor 的左侧积木图标里装的那个叫 "Python Extension" (Python 插件),而不是 Python 语言本身。

  • 作用

    • Cursor 本身只是一个高级记事本,它不懂 Python 语法。

    • 装了这个插件,Cursor 才能看懂 .py 文件,才能给你提供 代码高亮(关键字变色)、自动补全(输入 req 自动提示 requests)、错误检查(写错了画红波浪线)。

  • 比喻:这就像给汽车装上了仪表盘和导航仪。

3. 你在 Cursor 里选的是“钥匙” (Select Interpreter)

  • 你的疑问:“为什么要选解释器?”

  • 原因

    • Cursor 装好“仪表盘”后,它很迷茫。它会问:“老板,你的代码是想用哪一个发动机来跑?”

    • 你的电脑里可能有一个系统自带的 Python(不含依赖),还有一个 uv 刚刚建好的 Python(含有 requests 库)。

    • 如果你不告诉 Cursor 去选 uv 的那个(.venv),Cursor 可能会默认用系统的那个。

    • 后果:虽然你在终端用 uv run 能跑通(因为 uv 知道用哪个),但在代码编辑窗口里,Cursor 会给你画满红色的波浪线,提示“找不到 requests 库”,导致你无法享受自动补全。


一张图看懂关系

为了让你更直观地理解,我画了个简单的逻辑图:

角色

是谁?

作用

这里的“安装”是指?

UV

包工头

造房子:它下载了 Python.exe,建立了环境,安装了库。

安装真正的 Python 运行环境

Cursor

编辑器

写字台:提供打字的地方。

(它本身不需要安装 Python)

插件

Python Extension

翻译官:让编辑器能读懂 Python 代码语法。

安装 VS Code 的 功能增强包

配置

Select Interpreter

指路:告诉翻译官,“真正的 Python”在哪里。

不是安装,而是 指向 .venv 里的那个 exe。

总结

  • uv 是给代码用的:为了让程序能跑起来。

  • Cursor 里的设置是给你用的:为了让你在写代码的时候,有智能提示,不会满屏报错。

结论:

你并没有重复安装 Python。

  1. 你用 uv 安装了 Python (实体)

  2. 你在 Cursor 里安装了 Python 插件 (工具)

  3. 然后你把插件 连接 到了 uv 的 Python 上。

这就是为什么即便你不配置 Cursor,直接在终端敲 uv run main.py 也能跑(因为这绕过了编辑器),但为了写代码爽,你必须配置 Cursor。


这个问题的核心在于理解 Cursor(以及 VS Code)的**“记忆机制”**。

简短的回答是:可以修改,而且它是“跟着项目走”的。

这意味着:你为 A 项目设置了 Python 3.10,为 B 项目设置了 Python 3.12,Cursor 会自动记住它们各自的选择,互不干扰。你完全不用每次切换项目都重新设一遍。

下面我教你如何设置、修改,以及它背后的保存原理。


一、 如何设置和修改(标准动作)

无论你是第一次设置,还是后来想修改,操作步骤完全一样。

场景:你打开了一个项目(比如 D:\demo),想指定它使用 uv 创建的虚拟环境。

  1. 唤起命令面板:

    按键盘快捷键 Ctrl + Shift + P。

  2. 输入指令:

    输入 Python: Select Interpreter(通常输 select int 就能看到),然后回车。

  3. 做出选择 (关键一步):

    弹出的列表里会列出你电脑上所有的 Python。

    • 你要找的那个:通常带有 ('.venv': venv) 字样,或者路径是 .\.venv\Scripts\python.exe

    • 提示:Cursor 通常很聪明,会在这个选项旁边标一个星号 或者写着 Recommended(推荐),因为它检测到了你项目里有个虚拟环境。

  4. 确认成功:

    看 Cursor 窗口的右下角(状态栏)。应该会显示类似 3.12.x ('.venv') 的字样。


二、 它是如何实现“不同项目不同版本”的?

你可能会好奇,Cursor 把它记在哪里了?

当你为一个项目(比如 Project_A)选好解释器后,Cursor 会在这个项目文件夹里自动生成一个隐藏文件夹:.vscode

你可以去你的文件夹里看一眼,里面通常会有一个 settings.json 文件,内容大概是这样的:

JSON

{
    "python.defaultInterpreterPath": "d:\\demo\\.venv\\Scripts\\python.exe"
}

这就是秘密所在!

  • Project_A 的文件夹里:写着“请用 3.10”。

  • Project_B 的文件夹里:写着“请用 3.12”。

当你用 Cursor 打开不同的文件夹时,它会先读这个“小纸条”,然后自动切换到对应的 Python 版本。


三、 常见场景演练

为了让你更放心,我们预演一下你的工作流:

场景 1:切换不同项目

  • 早上:你打开 外Coin项目。Cursor 读到配置,自动加载 Python 3.10(因为这个库比较老)。

  • 下午:你打开 自动化脚本。Cursor 读到配置,自动加载 Python 3.12(因为你想用最新的语法)。

  • 结果:你什么都不用做,直接写代码就行,无缝切换

场景 2:uv 升级了 Python 版本

  • 动作:你在项目中运行了 uv python pin 3.11 把版本从 3.10 升到了 3.11,并运行了 uv sync 重建了环境。

  • Cursor 的反应

    • 有时候 Cursor 会自动发现变动,右下角自动变过来。

    • 如果它没变(还是红波浪线报错),你就按 Ctrl + Shift + P -> Select Interpreter,重新选一下那个新的 .venv 即可。


四、 极客小技巧:快速检查

在 Cursor 的右下角,你会看到当前使用的 Python 版本号。

  • 点击它:这就等同于按 Ctrl + Shift + P -> Select Interpreter。这是鼠标操作的快捷方式,非常方便。

总结:

放心大胆地建项目吧。只要你遵循 “每个项目用 uv init 或 uv sync 建立独立的 .venv”,然后在 Cursor 里选一次 .venv,它就会永远记住这个对应关系。


评论