背景回顾:物理分家之后在上一篇《我的工程目录架构1:基础目录结构与文件同步策略》中,我们将工程的“物理肉身”进行了明确的切割:• slns:我的原创代码(pyxllib, xlproject...)• slns+:参考的开源代码• data:数据文件物理上分好了,但紧接着迎来了第二个问题:代码怎么跑?
第一阶段:直接安装python
大部分初学者应该都是从这里开始的,我自己也是。去 Python 官网下载安装包 -> 一路 Next 安装 -> 打开 CMD pip install xxx -> 开始写代码。
在刚开始学习、手里只有几个简单项目时,这完全可行,甚至是最推荐的“无痛入门”方式。但随着时间推移,项目一旦多了,A 项目依赖 pandas 1.0,B 项目依赖 pandas 2.0,版本打架和全局环境污染会逼迫你寻找新的方案。
第二阶段:虚拟环境conda
为了支持每个项目可以创建独立隔离的运行环境,Anaconda 等相关工具应运而生,随后进入了群雄逐鹿的时代。
Conda 是很多人的首选,但它的问题在于太“重”:
- 臃肿Conda 的 base 环境动辄几个 GB,每个新环境都在吞噬你的硬盘空间。
- 龟速Solving environment 的转圈画面经常让人等到怀疑人生。
- 非标准Conda 体系与 Python 标准的 PyPI 体系始终有一层隔阂,有些包在 Conda 源里找不到,混用 pip 又容易坏环境。
为了解决 Conda 的问题,社区涌现了大量工具,但似乎总是在“按下葫芦浮起瓢”:
- Virtualenv / venv虽然标准,但管理起来太繁琐,需要手动激活、手动管理路径。
- Pipenv曾被誉为官方推荐,但那个长达数分钟的 Locking... 过程简直是生产力黑洞。
- Poetry一度非常流行,优雅地解决了依赖锁定和打包问题,但在处理极其复杂的依赖树时,解析速度依然感人,且非标准的配置方式让部分开发者感到水土不服。
- PDM引入了 PEP 582 的本地包目录概念,很新颖,但生态惯性难以改变。
很长一段时间里,Python 开发者都在苦苦寻求一个“完美”的解决方案。
第三阶段:全能工具 uv
直到 2024 年 2 月,uv 横空出世。如果说 2024 年大家还在犹豫“又要换工具吗?”,那么到了 2026 年,答案已经毫无争议:选 uv 就对了。
为什么 uv 能终结这场战争?
Rust 编写的极致速度:它不是快了一点,而是快了 10-100 倍。以前 pip 安装大包需要等喝杯咖啡,现在 uv 是秒级解析、秒级安装。它利用并发和底层优化,重新定义了 Python 的安装速度。
全局缓存与硬盘复用:这是 uv 的杀手锏。Conda 每个环境都复制一份包,而 uv 使用基于内容的全局存储。如果你的十个项目都用了 numpy,uv 实际上只在硬盘存了一份,其他项目都是毫秒级的硬链接(Hard Link)。这极大地解决了“环境隔离”带来的“磁盘爆炸”问题。
The All-in-One Toolchain:uv 不仅仅是一个快速的 pip。它整合了 pip (安装)、pip-tools (锁定)、pipx (工具运行)、poetry (项目管理)、pyenv (Python 版本管理)、twine (发布) 的所有功能。
以前我们需要 setup.py + pip + venv + twine 这一堆工具链,现在,只需要一个 uv。
uv 基础实战:给 pyxllib 一份“标准烹饪配方”很多人的项目文件夹只是一个装着 .py 文件的裸文件夹。对于 Python 工具链来说,它就像是一桌散落在案板上的原始食材,或者说是一道没有名字的“盲盒料理”。外界既不知道这堆食材最后要做成什么菜(项目元数据),也不知道具体需要搭配哪些佐料(依赖列表),更不知道正确的烹饪火候(构建系统)。
有人可能会说:“这有什么关系?炒菜嘛,缺什么佐料我就临时往锅里撒一点(pip install)嘛。反正这只是我给自己做的一顿快餐,或者以后要复用时,把这盘菜(把代码直接拷贝过去)端过去给别人尝尝不就行了?”
确实,如果只是写一个“阅后即焚”的一次性脚本,怎么方便怎么来完全没问题。但如果这是一个长期维护的功能,或者需要协作的项目,这种“凭感觉撒盐”的游击战法就是灾难的开始。试想一下,当你换了新电脑,或者把代码发给同事时,难道要让他们对着代码报错,靠舌头去反推你当初到底放了什么牌子的酱油、加了几勺糖?直接复制粘贴代码虽然看似省事,实则丢掉了最重要的“食谱”,给后续的复现带来无尽的麻烦。
为了解决这种“黑暗料理”的状态,使用 uv 进行现代化管理的第一步,就是给项目编写一份精确的“标准烹饪配方”——这就是 pyproject.toml。
这份文件就是你的后厨秘籍。它清晰地定义了这道菜的“菜名”(元数据)、“食材清单”(依赖关系)和“烹饪工序”(构建方式)。有了它,uv 这个“金牌帮厨”才能精准地照单备菜,哪怕换了一个厨房(新环境),也能还原出原汁原味的代码运行环境。
以我的核心工具库 pyxllib 为例简单介绍pyproject.toml:
# slns/pyxllib/pyproject.toml[project]name = "pyxllib"# 版本信息动态模式获取,不用每次发包都这里写"4.1.10"等硬编码dynamic = ["version"]description = "code4101的python通用工具库"requires-python = ">=3.6"# 项目依赖包dependencies = [ "python-dotenv", # 读取环境变量 "loguru", # 日志工具 "fire", # 制作命令行工具 "joblib>=1.3.2", # 并行处理工具,可以指定依赖版本 "uiautomation; sys_platform == 'win32'", # 可以指定只有windows系统才安装]# 构建系统:建议选择 hatchling,比 setuptools 更现代[build-system]requires = ["hatchling"]build-backend = "hatchling.build"[tool.hatch.version]# 版本信息从项目的__init__.py文件中的__version__变量直接读取path = "src/pyxllib/__init__.py"# 指定源码路径[tool.hatch.build.targets.wheel]packages = ["src/pyxllib", "src/pyxlpr"][tool.uv]# 改成清华源,会比官方pypi下载依赖性更快index-url = "https://pypi.tuna.tsinghua.edu.cn/simple"
有关pyproject.toml的配置写法,uv的用法,可以自行询问AI进一步学习。
核心策略:前线阵地(Frontline)与“实弹”演练紧接上文,我不急着给 pyxllib 单独创建环境, 因为 pyxllib 对我来说是一个“后方军火库”(Arsenal),而不是一个“前线战场”(Battlefield)。
我还有另一个项目 xlproject,它才是我日常工作的“前线阵地”。所有的实验性脚本、临时的业务逻辑、乃至最终交付的工程代码,大部分时间都是在 xlproject 这个战壕里“开火”运行的。
如果说 pyxllib 是我精心打磨的一批弹药和重型武器,那么 xlproject 就是我实战的阵地。战士不会住在军火库里,战士需要的是让后勤把弹药源源不断地输送到战壕里来。
在 uv 出现之前,要建立这种“前线直连后方”的补给线,通常需要用 pip install -e . (editable mode),但这更像是一条脆弱的土路,经常因为路径变动或环境切换而断供。而 uv 提供了一种极其优雅、声明式的解决方案:Workspaces(工作区)与本地依赖源。
3.1 制定“前线”作战部署
在 slns/xlproject 下,我同样建立了一个 pyproject.toml。这里才是真正定义我日常作战环境的地方。它的关键在于:如何告诉 uv,我需要的重武器 pyxllib 就在隔壁掩体,而且我要的是“实弹”。
来看看 slns/xlproject/pyproject.toml 的核心配置:
[project]name = "xlproject"requires-python = ">=3.12"dependencies = [ # 这里声明依赖 pyxllib "pyxllib", ][tool.uv.sources]# 这里解释直接建立本地补给线,而不是从pypi下载某个静态版本的pyxllibpyxllib = { path = "../pyxllib", editable = true }
注意那个 editable = true。这行配置是整个战略部署的灵魂。
它的含义是:uv 在构建 xlproject 的前线环境时,不会去 PyPI 请求空投(下载压缩包),也不会把 pyxllib 搬运复制到 site-packages 仓库里。相反,它建立了一条实时指向 ../pyxllib 物理路径的“高速补给通道”(软链接)。
这里只是用我的pyxllib/xlproject举例,读者完全可以根据这个原理,配置自己本地的多项目引用逻辑关系,对复杂依赖逻辑的项目也能实现精确便捷的环境配置与开发。
3.2 最终的兵力部署图
当我在 slns/xlproject 目录下执行 uv sync 后,uv 会完成环境同步/兵力集结,并在 xlproject 内部建立一个 .venv 指挥部。
此时的战局图看起来是这样的(逻辑视角):
slns/├── pyxllib/ <-- [Rear Arsenal] 后方军火库│ ├── src/│ └── pyproject.toml <-- 武器规格说明书│└── xlproject/ <-- [Frontline] 前线阵地 ├── .venv/ <-- [Command Center] 唯一的作战环境 │ └── lib/python3.10/site-packages/ │ ├── pandas/ <-- 外部盟军支援 (PyPI) │ └── pyxllib <-- 【高速通道】直连 ../../../pyxllib ├── analysis_scripts/ └── pyproject.toml <-- 作战部署计划
3.3 这种架构带来的实战优势
这套部署完成后,我获得了一个梦寐以求的战术闭环:
战术实时调整(Hot-Reloading): 我在后方 slns/pyxllib/debug_utils.py 里改进了瞄准算法,保存。 切到前线 slns/xlproject,直接扣动扳机 python main.py。 新算法立即生效! 我不需要重新打包运送(re-install),不需要漫长的等待。后方研发与前线实战在物理上是分离的,但在逻辑上通过 uv 实现了实时的连通。
统一指挥体系: 我不需要去维护 pyxllib 军火库里的空调温度(独立环境)。pyxllib 依赖的火药原料(比如 loguru),会被 uv 自动调配并安装到 xlproject 的前线环境里。 这意味着,我在“前线”作战时,拥有的是一个包含“业务特种兵 + 自研重武器 + 盟军支援”的全功能合成旅。
收编游击队(兼容 slns+): 还记得文章开头提到的 slns+(参考的开源代码)吗? 如果我想魔改一下 transformers 库(比如给它加个特殊的挂件),我只需要把 transformers 的源码 clone 到 slns+/transformers,然后在 xlproject 的部署计划里加一行: transformers = { path = "../../slns+/transformers", editable = true } 我的前线阵地瞬间就切换到了本地魔改版的 transformers,调试源码就像在擦拭自己的配枪一样简单。
通过 uv 的 sources 配置,我成功地将“物资存储”(slns/slns+ 分离)与“火力输出”(统一在 xlproject 执行)完美解耦。
不管我的后方有多少个仓库,在 uv 的指挥下,它们在战场上都汇聚成了同一股强大的火力。
3.4 全局火力覆盖:xlproject作为系统py默认环境
xlproject 类似以前的 conda base 环境。要让xlproject这个环境支持系统其他地方的运行,有以下一些方法。
1. 战术一:全域通行证(配置环境变量 PATH)
这是最符合 Windows 直觉的“主力环境”配置法,简单粗暴且高效。既然 xlproject 是你的主基地,那我们不妨直接把它的“军火库大门”向全系统敞开。
操作步骤:找到 xlproject 生成的虚拟环境脚本路径,比如:D:\home\chenkunze\slns\xlproject\.venv\Scripts将这个路径添加到 Windows 的 系统环境变量 Path 中(建议放在列表较前位置,优于其他 Python 安装路径)。
实战效果: 从此以后,你打开任意一个新的 CMD 或 PowerShell 窗口:输入 python:直接启动xlproject的 Python。输入 pip:直接管理xlproject的包。输入 uv:uv 依然可以在这个环境下正常工作。
假设你在桌面或任意文件夹下写了个 test.py,里面用了 pyxllib,直接输入 python test.py 即可运行。这完美复刻了当年安装 Anaconda 时勾选 "Add Anaconda to my PATH environment variable" 的那种全局掌控感,但没有任何臃肿的负担。
2. 战术二:临时征用(Activate 脚本)
如果你不想污染全局 PATH,或者偶尔需要在别的机器上操作,也可以使用传统的激活方式。 uv 创建的环境本质上是标准的 venv,所以在 Windows 下完全支持标准的激活脚本。
你可以在任何地方打开终端,执行类似下面的命令来“呼叫”主力部队:
# 在 CMD 中写后缀.bat,powershell中写后缀.ps1D:\home\chenkunze\slns\xlproject\.venv\Scripts\activate.bat
执行后,终端前缀会变成 (xlproject),此时你的所有操作都在xlproject内进行。这对于临时切换环境非常有用。
通过这两招,xlproject 就不再局限于某个文件夹,它变成了一层覆盖在整个 Windows 系统之上的“无形网络”,随时响应你的utools等其他场景的调用。
虽然 xlproject 作为主力部队非常强大,能解决我 80% 的日常需求,但总有一些项目是注定要“远航”的。
比如我最近正在开发一个名为 Freebill 的账单管理工具。它的目标不是只在我电脑上跑,而是要发布到 GitHub 给别人用,甚至打包成 exe 发给非技术人员。
这时候,如果还依赖庞大的 xlproject 环境就不合适了。我们需要从“主力”模式切换到“特遣队”模式。
4.1 第一阶段:孵化(在主基地快速实现原型)
在 Freebill 刚开始开发的时,我根本没有给它建专门的环境。 代码就放在 slns/xlproject/src/xlsln/ckz2026/Freebill 目录下,用 3.4 节 配置好的全局环境(xlproject)来运行它。
操作:直接在终端敲 python main.py。状态:此时 Freebill 就像是主基地里的一个“特种小队”,吃的是基地的粮(依赖),用的是基地的枪(环境)。优点:极速启动。不需要 uv init,不需要 uv sync,pandas、pyxllib 都是现成的,灵感来了立刻写代码,没有任何配置负担。
4.2 第二阶段:独立(构建最小依赖集)
当 Freebill 的功能稳定,准备发布到 GitHub 作为一个独立项目时,我必须给它一份精简的、独立的“生存清单”。我不能要求用户安装我那个巨大的 xlproject 环境。
我在 slns/Freebill 下创建专属的 pyproject.toml:
[build-system]requires = ["hatchling"]build-backend = "hatchling.build"[project]name = "freebill"description = "一个基于本地的账单聚合工具,将多源账单标准化存入 SQLite 并提供可视化的数据分析。"readme = "README.md"requires-python = ">=3.13"authors = [ { name = "chenkunze", email = "877362867@qq.com" }]classifiers = [ "Programming Language :: Python :: 3", "Operating System :: OS Independent",]dependencies = [ "pandas", "loguru", "python-dotenv", "openpyxl", "fire>=0.7.1", "nicegui>=3.5.0",]dynamic = ["version"][project.scripts]freebill = "freebill.main:cli"[tool.hatch.version]path = "src/freebill/__init__.py"
4.3 第三阶段:封疆(uv sync 生成独立环境)
接着,我在 Freebill 目录下执行:
uv sync
uv 扫描到当前目录有 pyproject.toml。它会在 slns/Freebill/.venv 创建一个全新的、隔离的虚拟环境。这个环境里只有loguru和pandas等必要依赖,没有 pyxllib 等无关杂物。这才是适合发布的干净环境。
4.4 两种模式的无缝切换
现在我的电脑上并存着两套逻辑,这也是 uv 最迷人的地方:
日常模式(主基地):在 CMD 里直接敲 python main.py。调用的是:PATH 中的 xlproject 全局环境。场景:我想随便改改代码,或者用我不打算打包进 Freebill 的库(比如用 matplotlib 画个临时图看看数据分布)时。
项目模式(独立特区):在slns/Freebill项目目录下敲 uv run python main.py。调用的是:Freebill/.venv 下的独立环境。场景:测试打包(PyInstaller)、CI/CD 流水线检查、确保没有用到未声明的依赖时。
总结: 平时用“大锅饭”(xlproject)来保证开发效率,发布时开“小灶”(独立 .venv)来保证工程质量。既不被虚拟环境的形式主义累死,又不在关键时刻掉链子。
至此,关于工程目录架构与 Python 环境管理的“内功心法”已经全部讲完。
虽然整篇文章是围绕我个人的三个项目——pyxllib(工具库)、xlproject(主基地)、Freebill(独立应用)——来展开的,但它本质上解决的是所有 Python 开发者都会面临的三大问题:
- 本地多项目间的动态依赖如何让库的修改在引用它的项目中实时生效,而不需要反复打包安装?(答案:uv sources 的 editable 模式)
- 多项目环境的高效治理如何在“隔离”与“复用”之间找到平衡点,既不让硬盘爆炸,又不让依赖打架?(答案:uv sync 的全局缓存与硬链接机制)
- 系统的默认兜底环境如何给操作系统提供一个功能全面、随叫随到的默认环境,且不污染linux这类系统自带 Python,兼容不影响已有的conda等旧项目环境配置?(答案:将 xlproject 作为新时代的 base 环境)
通过这套架构,我们建立了一套进可攻(独立发布)、退可守(快速原型)的正规军作战体系。
但这还只是故事的一半。
有了完美的“物理肉身”(目录结构)和“灵魂内功”(环境管理),我们还需要一把趁手的“神兵利器”来施展招式——那就是 IDE(VS Code / Trae)。
在下一篇文章我们将探讨:如何让编辑器与这套 uv 环境完美联动?如何打破“一个 Git 仓库就是一个项目”的刻板印象,把任意目录变成一个“聚焦工作区”,实现比 Git 仓库更精细的战术级操作?