2026 Python 项目管理实战:用 uv 替代 pip、venv 和 pipx
很多 Python 项目一开始都很简单:创建虚拟环境、安装依赖、运行脚本。可项目一多,就容易遇到这些问题:
venv在哪里创建不统一pip install后忘记更新requirements.txt- 本机 Python 版本和服务器不一致
ruff、pytest、httpie这类 CLI 工具混在项目依赖里- 新同事拉代码后不知道该执行哪几条命令
uv 的价值就在这里:它把 Python 版本、虚拟环境、依赖解析、锁文件、脚本运行和命令行工具管理放到一套命令里。本文不做概念堆叠,只给你一套可以直接复制的日常工作流。
完成后,你会掌握:
- 如何安装和验证
uv - 如何创建一个带锁文件的 Python 项目
- 如何用
uv run代替手动激活虚拟环境 - 如何运行单文件脚本
- 如何用
uvx管理临时 CLI 工具 - 如何把老项目从
requirements.txt迁移过来
什么场景适合优先用 uv
如果你正在维护下面这些项目,uv 很适合先引入:
- Python 脚本工具:爬虫、数据处理、日志分析、自动化运维
- Web 项目:FastAPI、Django、Flask
- AI 项目:LangChain、MCP Server、模型调用脚本
- CLI 工具:需要
ruff、pytest、mypy、httpie等开发工具 - 多人协作项目:希望依赖版本可复现
如果只是服务器上一个永远不改的老脚本,也不一定要马上迁移。更务实的方式是:新项目直接用 uv,老项目在下一次改依赖时再迁移。
方法 1:安装 uv 并确认环境
1. macOS 和 Linux 安装
官方安装脚本最直接:
curl -LsSf https://astral.sh/uv/install.sh | sh
安装后重新打开终端,或者手动加载 shell 配置:
source ~/.zshrc
如果你用的是 Bash,通常是:
source ~/.bashrc
2. Windows 安装
在 PowerShell 里执行:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
3. 验证安装
uv --version
uv help
能看到版本号和子命令说明,就说明安装完成。
常用命令可以先记住这几个:
uv init
uv add
uv remove
uv sync
uv run
uv lock
uvx
方法 2:创建一个新 Python 项目
下面用一个小型天气 CLI 项目做示例。它会请求公开接口,然后打印城市天气摘要。
1. 初始化项目
uv init weather-cli
cd weather-cli
查看生成的文件:
ls -la
你会看到类似结构:
.
├── .git
├── .gitignore
├── .python-version
├── README.md
├── main.py
└── pyproject.toml
.python-version 用来记录项目期望的 Python 版本,pyproject.toml 用来记录项目元数据和依赖。
2. 指定 Python 版本
如果你希望项目固定使用 Python 3.12,可以执行:
uv python install 3.12
uv python pin 3.12
再检查:
cat .python-version
预期输出:
3.12
3. 添加依赖
这个示例用 httpx 请求接口,用 rich 美化输出:
uv add httpx rich
执行后会更新两个关键文件:
pyproject.toml:记录直接依赖uv.lock:锁定完整依赖树
提交代码时,建议把这两个文件都提交。
方法 3:用 uv run 运行项目
很多人第一次用 uv 时,最不习惯的是不再手动执行:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
在 uv 项目里,直接运行:
uv run python main.py
uv run 会检查环境是否存在、依赖是否同步,然后在项目环境里执行命令。
1. 编写示例代码
把 main.py 改成下面这样:
from __future__ import annotations
import sys
import httpx
from rich.console import Console
from rich.table import Table
console = Console()
def fetch_weather(city: str) -> dict:
response = httpx.get(
"https://geocoding-api.open-meteo.com/v1/search",
params={"name": city, "count": 1, "language": "en", "format": "json"},
timeout=10,
)
response.raise_for_status()
data = response.json()
results = data.get("results", [])
if not results:
raise ValueError(f"city not found: {city}")
location = results[0]
weather = httpx.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": location["latitude"],
"longitude": location["longitude"],
"current": "temperature_2m,wind_speed_10m",
},
timeout=10,
)
weather.raise_for_status()
current = weather.json()["current"]
return {
"name": location["name"],
"country": location.get("country", ""),
"temperature": current["temperature_2m"],
"wind": current["wind_speed_10m"],
}
def main() -> None:
city = sys.argv[1] if len(sys.argv) > 1 else "Shanghai"
result = fetch_weather(city)
table = Table(title=f"Weather: {result['name']}, {result['country']}")
table.add_column("Metric")
table.add_column("Value")
table.add_row("Temperature", f"{result['temperature']} C")
table.add_row("Wind", f"{result['wind']} km/h")
console.print(table)
if __name__ == "__main__":
main()
运行:
uv run python main.py Shanghai
预期会看到一个表格,包含温度和风速。真实数值会随接口返回变化。
2. 不激活虚拟环境也能调试
你仍然可以看到 .venv:
ls -la .venv
但日常开发不需要手动激活它。直接用:
uv run python main.py Beijing
uv run python -m pip list
这能减少“我到底在哪个虚拟环境里”的混乱。
方法 4:把开发工具放进 dev 依赖
项目运行依赖和开发工具应该分开。例如 httpx 是运行依赖,ruff 和 pytest 更适合作为开发依赖。
添加开发依赖:
uv add --dev ruff pytest
运行格式和检查:
uv run ruff format .
uv run ruff check .
创建一个简单测试:
mkdir -p tests
cat > tests/test_smoke.py <<'EOF'
from main import fetch_weather
def test_fetch_weather_shanghai():
result = fetch_weather("Shanghai")
assert result["name"]
assert "temperature" in result
EOF
运行测试:
uv run pytest
如果你不希望测试依赖真实网络,可以把 fetch_weather 拆成更小函数,并用 mock 替代 HTTP 请求。这里为了让示例短,保留真实请求版本。
方法 5:用 uvx 临时运行 CLI 工具
有些工具只是偶尔用一次,不应该写进项目依赖。例如你想临时格式化或检查一个目录:
uvx ruff check .
也可以临时运行第三方 CLI:
uvx httpie --version
uvx pycowsay "hello uv"
如果某个工具每天都用,可以安装成全局工具:
uv tool install ruff
ruff --version
简单判断规则:
- 项目必须复现的工具:用
uv add --dev - 偶尔运行一次的工具:用
uvx - 每天在多个项目都用的工具:用
uv tool install
方法 6:迁移已有 requirements.txt 项目
假设老项目结构是这样:
old-project
├── app.py
└── requirements.txt
requirements.txt 内容:
fastapi==0.115.0
uvicorn[standard]==0.30.6
1. 在原目录初始化
进入项目目录后执行:
uv init --bare
--bare 适合已有项目,它不会生成示例 main.py。
2. 导入依赖
uv add -r requirements.txt
然后同步环境:
uv sync
3. 改造启动命令
以前可能是:
source .venv/bin/activate
uvicorn app:app --reload
迁移后可以改成:
uv run uvicorn app:app --reload
部署脚本里也建议用 uv sync --frozen,确保线上环境严格使用锁文件:
uv sync --frozen
uv run uvicorn app:app --host 0.0.0.0 --port 8000
验证:确认项目可复现
完成上面步骤后,用下面这组命令检查项目状态:
uv lock --check
uv sync --frozen
uv run python --version
uv run python main.py Shanghai
如果这些命令都能执行,说明项目的依赖声明、锁文件和运行入口基本可用。
建议提交这些文件:
git add pyproject.toml uv.lock .python-version main.py README.md
git commit -m "Use uv for Python project workflow"
团队协作时,新成员只需要:
git clone <repo-url>
cd weather-cli
uv sync
uv run python main.py Shanghai
常见问题和修复
1. 找不到 uv 命令
错误类似:
zsh: command not found: uv
先重新打开终端。如果仍然不行,检查安装目录是否在 PATH 里:
echo "$PATH"
macOS 和 Linux 上也可以重新执行安装脚本,然后按提示加载 shell 配置。
2. uv add 提示找不到 pyproject.toml
错误通常是因为你不在项目目录里。
修复:
cd weather-cli
uv add requests
如果这是一个已有项目,先执行:
uv init --bare
3. Python 版本不一致
先查看项目要求:
cat .python-version
安装并同步:
uv python install
uv sync
如果你想明确切到 3.12:
uv python install 3.12
uv python pin 3.12
uv sync
4. 锁文件和依赖声明不一致
如果 uv lock --check 失败,说明 pyproject.toml 和 uv.lock 不匹配。
开发环境里更新锁文件:
uv lock
生产环境不要自动改锁文件,应该失败后回到开发机修复并重新提交。
5. 不知道该用 uv add 还是 uv pip install
日常项目开发优先用:
uv add package-name
这会更新 pyproject.toml 和 uv.lock。
uv pip install 更适合兼容传统 pip 工作流,例如临时维护旧环境。新项目建议尽量围绕 uv init、uv add、uv sync 和 uv run 建立流程。
总结
uv 最实用的地方不是“命令更少”,而是把 Python 项目的关键流程统一起来:创建项目、锁定依赖、同步环境、运行命令、管理工具。对个人项目,它能减少虚拟环境和依赖文件的混乱;对团队项目,它能让新机器更快复现同一个运行环境。
建议新项目直接从 uv init 开始,老项目则先从 uv init --bare 和 uv add -r requirements.txt 小步迁移。只要把 pyproject.toml、uv.lock 和 .python-version 提交好,后续运行基本就能收敛到一条命令:uv run。
- 原文作者:春江暮客
- 原文链接:https://www.bobobk.com/uv-python-workflow.html
- 版权声明:本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。