写在前面
时隔三年重新开始写博客。这三年经历了读研、出国和考博,也曾多次想把过去没有写完的内容继续整理下来,但最终一直没有真正开始。索性将之前的文章清理掉,从头重新记录。
写这篇文章最直接的原因,是想给组里的师兄、师姐、师弟和师妹整理一份相对完整的 Python 环境配置教程,同时也介绍一些常见机器学习项目中比较实用的环境管理方法。
对于科研工作而言,环境配置本身并不是研究的核心,却经常消耗大量时间。Python 版本不一致、依赖冲突、虚拟环境混乱、深度学习框架安装失败,这些问题对于刚接触 Python 的同学尤其常见。
因此,本文希望尽可能提供一套简单、统一且具有较好可复现性的 Python 项目管理方式。按照本文的流程进行配置后,大多数常规科研项目都可以使用相似的方法完成环境初始化与依赖管理,尽量避免让“配置环境”成为科研工作的额外负担。
0 - pip 是什么,uv 又是什么?
Python 自带大量标准库,用于提供文件操作、系统接口、JSON 解析等基础功能,例如 io、sys 和 json。但在机器学习、数据分析和科学计算中,我们往往还需要使用大量第三方库,例如 NumPy、Pandas、scikit-learn 和 PyTorch 等。
在大多数 Python 安装环境中,pip 是最常见的第三方包管理工具。通过一条简单的命令:
1 | pip install <package> |
即可从 PyPI(Python Package Index)下载并安装所需的第三方库。
这种方式非常方便,但在实际科研项目中也存在一些问题。
首先,如果直接在全局 Python 环境中执行 pip install,不同项目所需的依赖会逐渐混杂在同一个环境中。当两个项目依赖同一个库的不同版本时,就可能出现版本冲突。此时往往需要反复执行安装、卸载或降级操作,不仅增加环境维护成本,也会降低项目的可复现性。
当然,一个自然的解决方案是:为每个项目建立独立的虚拟环境。
但新的问题随之出现。例如,在多个机器学习项目中分别创建虚拟环境后,PyTorch 等体积较大的依赖可能会被重复下载和存储。随着项目数量增加,环境初始化时间和磁盘占用都会逐渐成为问题。
如果能够将已经下载过的包统一缓存,并在不同项目之间复用,就可以显著减少重复下载和安装开销。
这正是 uv 的优势之一。
uv 的官方定位是:
An extremely fast Python package and project manager, written in Rust.
即:一个使用 Rust 编写的高性能 Python 包与项目管理工具。
官方提供的性能测试如下:

与传统的 Python 工具链相比,uv 将过去由多个工具分别承担的功能进行了较大程度的整合。例如,它可以承担许多原本由 pip、pipx、Poetry、virtualenv 和 pyenv 等工具完成的工作,包括:
- Python 版本管理;
- 虚拟环境创建;
- 项目初始化;
- 依赖安装与解析;
- 依赖版本锁定;
- 包缓存与复用;
- Python 脚本和项目命令运行。
与此同时,uv 使用 Rust 实现,并针对依赖解析、下载和缓存机制进行了优化。在许多典型场景下,其执行速度可以显著快于传统 Python 包管理工具。
对于科研项目而言,它最大的价值并不仅仅是“快”,而是能够提供一套相对统一的 Python 项目管理方式。
1 - 安装 uv
uv 可以通过 pip 安装,也可以直接安装而不依赖现有 Python 环境。
这里更推荐直接安装。
原因很简单:如果 uv 本身依赖某个已有 Python 环境,那么一旦该 Python 环境出现问题,uv 的可用性也可能受到影响。直接安装可以减少这种额外依赖,使其作为独立的环境管理工具运行。
在 Windows 下,可以使用官方安装脚本。以 PowerShell 运行:
1 | powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" |
如果安装过程中出现网络相关错误,可以尝试更换网络环境后重新执行。
除此之外,我个人更推荐使用 Scoop 安装。这样不仅安装过程较为简单,后续的软件更新与管理也更加方便。
首先安装 Scoop:
1 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser |
然后安装 uv:
1 | scoop install main/uv |
安装完成后,新开一个命令行窗口,运行:
1 | uv self version |
如果能够正常输出版本信息,即说明 uv 已成功安装。
前面提到,uv 会使用缓存机制减少不同项目之间的重复下载和存储。对于经常运行大量机器学习项目的电脑,可以考虑单独规划缓存目录。
例如,如果大多数代码和项目都位于 D 盘,可以在 D 盘创建:
1 | D:/UvCache |
作为 uv 的缓存目录。
在项目目录与缓存目录位于同一磁盘的情况下,通常更有利于减少跨磁盘的数据复制开销。
例如可以指定:
1 | uv cache dir --cache-dir D:/UvCache |
随后可以通过:
1 | uv cache dir |
查看当前使用的缓存目录。
2 - 初始化项目
完成安装后,就可以开始使用 uv 管理 Python 项目。
对于已有项目,建议首先检查项目根目录下是否存在 .venv、.env 或其他由 IDE、Conda、virtualenv 等工具创建的旧虚拟环境。
如果准备完全切换到 uv 管理,并且确认这些旧环境中没有需要保留的内容,可以将其删除后重新初始化,避免不同环境管理方式相互干扰。
2.1 已存在 pyproject.toml
如果项目根目录已经存在:
1 | pyproject.toml |
通常只需要在项目根目录执行:
1 | uv sync --all-extras |
uv 会根据项目配置和锁文件完成 Python 环境及项目依赖的同步。
对于配置规范的项目而言,通常不需要再手动逐个安装依赖。
当然,这一过程的前提是项目自身的 pyproject.toml 和依赖声明是正确的。如果项目配置本身存在问题,则需要进一步检查依赖版本或项目配置,这部分不属于本文讨论重点。
2.2 只有 requirements.txt
如果项目没有 pyproject.toml,但存在传统的:
1 | requirements.txt |
可以首先执行:
1 | uv init |
初始化项目。
如果已经明确知道项目所需的 Python 版本,也可以直接指定:
1 | uv init --python <Python版本号> |
随后将原有 requirements.txt 中的依赖加入项目:
1 | uv add -r requirements.txt |
至此,一个传统的 requirements.txt 项目就可以逐步迁移到基于 pyproject.toml 和 uv.lock 的依赖管理方式。
2.3 什么都没有
还有一种情况:某些代码仓库既没有 pyproject.toml,也没有 requirements.txt。
这类项目的环境复现通常会更加困难。
可以首先阅读项目的 README.md,检查作者是否说明了 Python 版本和依赖要求;随后查看主要 Python 文件顶部的 import 语句,大致判断项目依赖了哪些第三方库。
确定 Python 版本后,可以运行:
1 | uv init --python <Python版本号> |
随后添加所需依赖:
1 | uv add <lib1> <lib2> <lib3> |
也可以使用 ==、>=、<= 等方式约束依赖版本,例如:
1 | uv add "numpy>=2.0" "pandas<3.0" |
对于缺少任何环境说明的项目,依赖恢复本质上属于“环境考古”。如果项目年代较久,还可能涉及已经弃用的库、旧版 Python 或相互冲突的依赖,因此需要根据实际报错逐步处理。
3 - 依赖管理和运行代码
前面多次提到了 pyproject.toml。那么,它究竟是什么?
简单来说,pyproject.toml 是现代 Python 项目中非常重要的统一配置文件。
过去,一个 Python 项目中可能同时存在:
1 | setup.py |
等多个配置文件,不同工具分别读取不同配置,项目结构容易变得较为分散。
pyproject.toml 的引入,使 Python 项目能够通过一个标准化入口描述构建系统、项目元数据、依赖、开发工具配置等信息。配合 uv 后,还可以进一步统一 Python 版本、依赖解析与虚拟环境管理。
下面是一份典型的 pyproject.toml:
1 | [project] |
对于一般科研项目,我比较推荐采用类似的组织形式。
其中:
[project]用于描述项目基本信息及核心依赖;[project.scripts]可以为常用程序入口定义命令别名;[project.optional-dependencies]可以存放开发、测试等非核心依赖;[build-system]用于配置项目构建方式;[tool.uv]用于定义 uv 相关配置;[tool.ruff]和[tool.mypy]分别用于配置 Ruff 和 MyPy。
这里额外加入了 Ruff 和 MyPy 两个开发工具。
Ruff 可以用于代码检查、导入排序和格式化,MyPy 可以用于静态类型检查。对于需要长期维护、多人协作或者准备公开代码的科研项目,这类工具能够显著提升代码规范性。
常用命令包括:
1 | uv run ruff check <dir/code> |
1 | uv run ruff format <dir/code> |
以及:
1 | uv run mypy <dir/code> |
如果需要查看完整参数,可以使用:
1 | uv run ruff --help |
或:
1 | uv run mypy --help |
一套规范的配置如果再配合合理的目录结构,可以进一步提升科研项目的可维护性。
例如:
1 | example/ |
在日常使用中,uv 的依赖管理主要可以归纳为三个命令:
uv add <package>:添加依赖;uv remove <package>:删除依赖;uv sync --all-extras:根据项目配置同步环境。
执行:
1 | uv add <package> |
时,如果没有主动指定版本约束,uv 会根据当前 Python 环境和依赖关系解析合适的版本,并将依赖信息写入 pyproject.toml,同时更新 uv.lock。
uv.lock 的作用尤其重要。
pyproject.toml 更多描述的是“允许使用哪些版本”,而 uv.lock 会记录经过解析后实际使用的具体依赖版本。将它提交到 Git 后,其他人在重新执行 uv sync 时,就更容易获得与原项目一致的依赖环境。
这对于科研代码复现非常重要。
运行程序时,同样可以通过 uv:
1 | uv run <code> |
例如:
1 | uv run python main.py |
如果已经在 [project.scripts] 中定义了命令:
1 | [project.scripts] |
则可以直接执行:
1 | uv run pre_process |
这样可以避免记忆复杂的脚本路径,也能使项目的标准运行入口更加清晰。
4 - 与 IDE 集成
接下来讨论如何在 IDE 中使用 uv。
实际上,这一步通常不需要复杂配置。
uv 默认会在项目根目录创建:
1 | .venv |
作为当前项目的虚拟环境。
目前常用的 Python IDE 和编辑器,例如 PyCharm 和 VS Code,都可以识别项目目录中的虚拟环境。因此,在使用 uv 初始化并同步项目环境后,直接通过 IDE 打开项目目录即可。
如果 IDE 没有自动选择正确的解释器,只需要在 Python 解释器设置中手动选择项目根目录中的 .venv。
Windows 下通常为:
1 | .venv\Scripts\python.exe |
Linux/macOS 下通常为:
1 | .venv/bin/python |
即可。
至于 IDE 的选择,主要还是取决于个人使用习惯和项目复杂度。
PyCharm 提供了较为完整的 Python 开发能力,在大型工程中优势明显,但相对而言资源占用也更高。对于组内大多数数据分析、机器学习实验和科研脚本,我个人更倾向于使用 VS Code:启动速度快、扩展生态丰富,同时足以覆盖大多数科研开发需求。
这里推荐一个 VS Code 的 Python 环境管理扩展:
如果电脑中同时存在 uv、Conda、Poetry 等多种环境管理工具,该扩展可以帮助统一查看和切换不同 Python 环境。
5 - PyTorch 安装
机器学习项目中,相对容易出现环境问题的框架之一是 PyTorch。
与普通 Python 库不同,PyTorch 的安装通常还涉及操作系统、硬件平台以及 CUDA 版本等因素。尤其是在使用 NVIDIA GPU 时,需要确保安装的 PyTorch 构建版本与目标 CUDA 环境相匹配。
uv 支持为特定依赖配置独立的软件包索引,因此可以直接在 pyproject.toml 中指定 PyTorch 官方提供的对应 CUDA Wheel 源。
下面以 CUDA 13.0 对应的 cu130 为例:
1 | [[tool.uv.index]] |
配置完成后执行:
1 | uv add torch torchvision |
即可将 PyTorch 和 TorchVision 加入当前项目。
如果需要使用其他 CUDA 版本,则根据 PyTorch 官方提供的 Wheel 索引,将上述配置中的 cu130 调整为对应版本即可。
采用这种方式的一个优势是:PyTorch 的来源信息直接记录在项目配置中。
相比于仅仅在 README 中写一句:
1 | 请安装 CUDA 版本的 PyTorch |
将具体的软件源写入 pyproject.toml 更有利于其他人在复现项目时得到一致的运行环境,也能够减少因安装错误构建版本而导致的问题。
6 - 结语
Python 环境配置本身并不是一个复杂的研究问题,但它往往会在实际科研过程中产生大量额外成本。
传统工作流中,我们可能需要分别处理 Python 版本、虚拟环境、依赖安装、依赖锁定以及项目运行等问题。当项目数量增加后,不同工具和环境之间的差异很容易导致依赖冲突、版本不一致以及代码难以复现。
uv 的价值就在于将这些原本相对分散的工作整合到一套更加统一的工作流中。
从项目初始化:
1 | uv init |
到添加依赖:
1 | uv add <package> |
再到同步环境:
1 | uv sync |
以及运行代码:
1 | uv run |
大多数常见的 Python 项目管理任务都可以通过相对一致的方式完成。
对于科研工作而言,我认为工具是否“先进”并不是最重要的。更重要的是,它能否降低环境维护成本,提高项目的可复现性,并让研究者将更多时间用于数据、模型、实验和论文,而不是反复处理软件环境问题。
从这个角度来看,uv 提供了一套值得尝试的现代 Python 项目管理方案。
刚开始使用时可能需要一定的适应过程,但一旦建立起统一的项目结构和依赖管理习惯,后续无论是自己维护代码、迁移计算设备、与组内同学协作,还是公开科研项目,都会方便很多。
希望这篇教程能够帮助大家少花一些时间配置环境,多花一些时间解决真正值得研究的问题。
最后,祝大家科研顺利,代码稳定,实验可复现,论文高产。