UV:Rust重写的快速Python包管理器
前段时间,无意中了解到一个名为 UV
的 Rust 重写的快速包管理器,感觉非常有趣,为什么不用pip呢,各种包管理器的区别和联系、优缺点在哪里?这段时间用下来,感觉非常不错,速度快、功能强大、使用简单,版本依赖管理尤其突出。
Windows用户使用管理员模式的powershell执行下面的命令安装即可,如果有报错问问GPT就可以了,多半是权限的问题。
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
其他方式和操作系统安装参考:https://docs.astral.sh/uv/getting-started/installation/#standalone-installer
简单尝试
Python管理
反正就 uv python
+ 命令,一般是先list
看哪些版本可以下,find
看安装了什么版本,install
安装,uninstall
卸载,pin
特定版本到项目。
安装最新版cpython
uv python install [版本号] --reinstall(可选,覆盖安装)
升级python版本
uv python upgrade
代理执行
注
前提是.py文件中导入的包为标准类库,例如 import os
、import sys
等等
uv run <python文件> [参数]
有特定依赖项使用以下命令:
uv run <python文件> --with <依赖项> [参数]
显式声明依赖
2023年10月,python官方发布了PEP 723,引入了声明式内敛元数据以及
pyproject.toml
文件来声明项目的依赖项。
初始化
uv init --script <python文件> --python <python版本>
声明脚本依赖,这里向 example.py
文件添加了 requests
和 rich
这两个依赖项,版本限制为小于3的版本。
uv add --script example.py 'requests<3' 'rich'
修改下载地址(包索引)
如果你想使用其他镜像源,可以通过以下命令修改包索引地址:
uv add --index "https://example.com/simple" --script example.py 'requests<3' 'rich'
会一并写入 pyproject.toml
文件中。
# [[tool.uv.index]]
# url = "https://example.com/simple"
到这里,我们就可以看出它的长处了,可以显示定义下载源,版本依赖,甚至可以指定脚本文件。
锁定脚本依赖
与项目不同,脚本依赖是指特定脚本所需的依赖项。可以通过以下命令锁定脚本依赖:
uv lock --script example.py
生成的结构如下
test.py
test.py.lock
import site
import sys
import ultralytics
version = 1
revision = 2
requires-python = ">=3.13"
使用不同python
uv run <python文件> --python <python版本> [参数]
UV工具
uvx
只有当在无关紧要的测试、扁平化项目中用这个比较合适,uvx
等价于下面的命令:这样做是为了预先测试检查兼容python包版本,你知道的,python的包管理就和重庆的天气一样糟糕(
uv tool run
$ uvx pycowsay nb
--
< nb >
--
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
这个用的比较少,暂时写到这里。链接:https://docs.astral.sh/uv/guides/tools/#running-tools
项目管理常用
uv init hello-world
cd hello-world
生成的目录结构如下:
.gitignore
.python-version
README.md
main.py
pyproject.toml
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
3.13
def main():
print("Hello from hello-world!")
if __name__ == "__main__":
main()
[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []
项目结构
.venv
Scripts
include## 包含的头文件
lib## 所有的包安装位置
...
python.exe## 当前版本python执行程序
pyvenv.cfg
.python-version
README.md
main.py
pyproject.toml
uv.lock
pyproject.toml
查看所有字段
[project]
name = "hello-world" ##项目名称
version = "0.1.0" ##项目版本
description = "Add your description here" ##项目描述
readme = "README.md" ##项目说明文件
requires-python = ">=3.13" ##需要的python版本
dependencies = [] ## 依赖项列表
现在我们来试试安装yolo的依赖项,PYPI的镜像站点下载最好把代理关了或者设置镜像。
uv add ultralytics
非常快,10秒内完成几百兆的文件下载和索引!(不过网速足够可能才行)
变化如下:
[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"matplotlib>=3.10.3",
"tqdm>=4.67.1",
"ultralytics>=8.3.168",
]
.python-version
告诉项目使用的Python版本号,初始化后自带,也可自定义
.venv
项目的虚拟环境,即与系统其余部分隔离的 Python 环境。这是 uv 将安装项目依赖项的位置。
uv.lock重要
uv.lock 是一个跨平台的 lockfile,其中包含有关项目依赖项的确切信息,类似与npm中的package.lock
或pnpm.lock
,由uv自动化控制和生成,不要修改它。它可以:
- 声明项目的开发平台
- 声明项目所需的Python版本
- 声明包及其依赖的下载地址、文件哈希等
文件哈希
通过哈希校验算法确认包未被篡改,保障包的完整性和安全性。
这样做的好处是让不同平台的开发者在安装依赖时获得相同的结果,确保项目在不同环境中的一致性。(哇,安装包很头疼的好吗?)
坏处是若下载地址失效或包被删除,可能会导致安装失败,可以手动尝试修改下载地址。
```lock title="uv.lock"
version = 1
revision = 2
requires-python = ">=3.13"
resolution-markers = [
"sys_platform == 'win32'",
"sys_platform == 'darwin'",
"platform_machine == 'aarch64' and sys_platform == 'linux'",
"(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')",
]
[[package]]
name = "certifi"
version = "2025.7.14"
source = { registry = "https://mirrors.cernet.edu.cn/pypi/web/simple" }
sdist = { url = "https://cmcc.mirrors.ustc.edu.cn/pypi/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" }
wheels = [
{ url = "https://cmcc.mirrors.ustc.edu.cn/pypi/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" },
]
添加、移除依赖
add、remove
uv add requests
uv remove requests
# Specify a version constraint
uv add 'requests==2.31.0'
# Add a git dependency, 手动指定
uv add git+https://github.com/psf/requests
从requirements.txt中同步
在常见的pip管理的项目中,我们会用到 requiremts.txt
来管理项目,uv支持迁移
uv add -r requirements.txt -c constraints.txt
升级某个包
正常的流程是先锁定其他包的版本,再执行某个包的升级任务
uv lock --upgrade-package <package>
查询项目版本
uv version
hello-world 0.1.0
uv version --short
0.1.0
uv version --output-format json
{
"package_name": "hello-world",
"version": "0.1.0",
"commit_info": null
}
同步依赖
一般而言,写好代码后为了运行起来,新增的依赖我们自然而然就把依赖装好了,比如 uv add
。但是如果是团队协作中,其他人可能没有安装你新增的依赖(反之亦然),这时候就需要同步依赖了。
uv sync # 同步依赖到uv.lock和pyproject.toml
.venv\Scripts\activate ## windows
source .venv/bin/activate ## linux/macOS
flask run -p 3000
python example.py
注
激活环境后,无需再使用 uv
前缀,直接使用 python
命令即可运行脚本。
造轮子
uv build
默认在当前目录生成dist
目录,里面包含了打包好的文件。
dist
.venv
Scripts
include
lib
python.exe
pyvenv.cfg
dist
hello_world-0.1.0-py3-none-any.whl// wheel包,也就是所谓的轮子
hello_world-0.1.0.tar.gz// 源码包
hello_world.egg-info// hutch包信息
dependency_links.txt
PKG-INFO
requires.txt
SOURCES.txt
top_level.txt
main.py
pyproject.toml
uv.lock
README.md
.python-version
.gitignore
LICENSE
发布包
升级项目版本号
uv version n.e.w
平级升级,uv划分的标准从大到小为:major, minor, patch, stable, alpha, beta, rc, post, and dev
# 从稳定版到预发布版
uv version --bump patch --bump [alpha/beta/rc/post/dev]
# 预发布版的内部升级
uv version --bump [alpha/beta/rc/post/dev]
# 预发布版到稳定版
uv version --bump stable
同步注意
执行 uv version
后会自动更新 pyproject.toml
和 uv.lock
文件中的版本号。不升级请执行 --frozen
参数
迁移
pip2uv
我们都知道,pip使用requirements.txt来管理依赖。例如,写入当前项目的依赖使用:
环境隔离
考虑使用venv虚拟环境来隔离项目依赖,避免某个Python版本全局安装包的冲突(想哭了😭)。
python -m venv
source .venv/bin/activate
pip ...
其中一种管理依赖的方式是pip-compile
编译规则集 requirements.in 文件
例如,requirements.in
文件内容如下:
fastapi
pydantic>2
表示 fastapi
无版本约束, pydantic
版本大于2.0.0。
执行下面的命令来生成 requirements.txt
文件:
pip-compile requirements.in -o requirements.txt
annotated-types==0.7.0
# via pydantic
anyio==4.8.0
# via starlette
fastapi==0.115.11
# via -r requirements.in
idna==3.10
# via anyio
pydantic==2.10.6
# via
# -r requirements.in
# fastapi
pydantic-core==2.27.2
# via pydantic
sniffio==1.3.1
# via anyio
starlette==0.46.1
# via fastapi
typing-extensions==4.12.2
# via
# fastapi
# pydantic
# pydantic-core
这个等同于 uv pip compile
,也是 pip-tools
中的 pip-compile
命令。
另外一种比较不常见的方式是使用 pip freeze
命令来生成当前环境的依赖列表。
pip freeze > requirements.txt
annotated-types==0.7.0
anyio==4.8.0
fastapi==0.115.11
idna==3.10
pydantic==2.10.6
pydantic-core==2.27.2
sniffio==1.3.1
starlette==0.46.1
typing-extensions==4.12.2
豪了,有以上的思路,我们就可以:
划分各种依赖环境
比如项目有 dev | prod | staging 等环境,我们可以将依赖分为不同的文件,例如
requirements-dev.in
、requirements-prod.in
等。编写这些文件中的特殊依赖
写好后分别保存到
requirements-dev.in
和requirements-prod.in
文件中。使用
uv pip compile
命令编译每个环境的依赖uv pip compile requirements-dev.in -o requirements-dev.txt # 或者 pip-compile requirements-dev.in -o requirements-dev.txt uv pip compile requirements-prod.in -o requirements-prod.txt
安装依赖
pip install -r requirements-dev.txt pip install -r requirements-prod.txt
上面讨论的同一个项目中不同开发环境的依赖管理。再扩展下,不同的操作系统也是一样的。
不过 uv 支持跨平台解析,非常方便,比如 tqdm
tqdm
两个平台的解析为
tqdm==4.67.1
# via -r requirements.in
colorama==0.4.6
# via tqdm
tqdm==4.67.1
# via -r requirements.in
colorama: Windows平台下的tqdm需要colorama包来支持终端颜色输出
这样每个平台间是不兼容的,
如果使用 uv 的 universal
选项 ,则会声明colorama的平台:
uv pip compile --universal requirements.in
colorama==0.4.6 ; sys_platform == 'win32' # 这里就声明了
# via tqdm
tqdm==4.67.1
# via -r requirements.in