Python 生态渐好,同一件事有很多工具可选,搭建环境时不知选谁为好,且往往要为解释器版本、虚拟环境创建、依赖管理单独下载工具,难免希望有一个All in One的工具。
这篇文章整理一套日常开发工具链:
- 用
uv管 Python 版本、虚拟环境和依赖。 - 用 Ruff 负责格式化、导入排序和静态检查。
- 用
pyproject.toml放项目元数据和工具配置。 - 用 VS Code + Pylance 负责编辑器补全、跳转和类型提示。
uv 吸引人的地方首先是快,但更重要的是它把过去分散在 pip、pip-tools、virtualenv、pipx、pyenv 和部分项目管理工具里的能力收束到一个命令下。
安装 uv
官方文档入口是 uv docs。独立安装器不依赖本机已有 Python,更适合作为第一选择。
curl -LsSf https://astral.sh/uv/install.sh | shpowershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"管理 Python 版本
Python 项目常见的隐性问题,是作者本地用 3.12,CI 或同事机器用 3.10。语法、标准库、类型注解和依赖解析都可能因此不同。# 查看可用版本uv python list# 安装指定版本uv python install 3.12# 在当前项目固定版本uv python pin 3.12
uv python pin 会写入 .python-version。之后在项目目录里执行 uv run、uv sync 等命令时,uv 会优先按这个版本找解释器;找不到且允许下载时,会自动下载对应版本。
还可以在 pyproject.toml 里声明项目支持的 Python 范围:[project]requires-python = ">=3.12"
.python-version 和 requires-python 各管一层:文件 / 字段 作用 .python-version本地工作目录偏好的解释器版本,偏向开发体验 project.requires-python项目声明支持的 Python 版本范围,偏向包元数据和依赖解析
个人项目里可以两者都写。例如要求项目支持 >=3.12,本地当前固定在 3.12 或 3.13。
创建项目
新项目推荐直接用 uv init:uv init my-appcd my-app
如果当前目录已经是项目目录:uv init
运行示例程序:uv run python main.py
第一次执行项目命令时,uv 会创建 .venv、解析依赖,并生成 uv.lock。和手动激活虚拟环境相比,uv run 的好处是命令自带上下文:你明确在项目环境里运行,而不依赖当前 shell 是否已经激活了正确环境。
例如:uv run python --versionuv run python -c "import sys; print(sys.executable)"
第二条命令应该输出项目 .venv 里的 Python 路径。这是检查环境是否正确的最直接方法。
虚拟环境:要不要手动激活
uv 支持两种使用方式。
第一种是不激活环境,所有命令都加 uv run:uv run python main.pyuv run pytestuv run ruff check .
这适合脚本、CI 和文档,因为命令自带上下文,不依赖 shell 状态。
第二种是激活 .venv,之后直接运行命令:
source .venv/bin/activatepython main.pydeactivate.\.venv\Scripts\Activate.ps1python main.pydeactivateoverlay use .venv/bin/activate.nupython main.pydeactivateWindows 下 Nushell 的路径通常是:
overlay use .venv/Scripts/activate.nu我的习惯是:日常终端里可以激活,文档和自动化脚本里尽量用 uv run。这样不会把「我当前 shell 的状态」变成项目构建的一部分。
添加、更新和删除依赖
# 添加运行时依赖uv add requests# 添加开发依赖uv add --dev pytest ruff# 指定版本uv add "ruff==0.8.0"uv add "fastapi>=0.115"# 删除依赖uv remove requestsuv remove --dev ruff# 查看依赖树uv tree注意区分 uv add 和 uv pip install:命令 适合场景 是否写入项目依赖 uv add <pkg>项目依赖管理 是,写入 pyproject.toml 并更新锁文件uv pip install <pkg>兼容旧的 pip 工作流或临时环境操作 通常不改项目元数据 uv run --with <pkg> <cmd>临时带一个包运行命令 不持久写入项目
例如临时运行一个工具:uv run --with rich python -c "from rich import print; print('[green]hello[/green]')"
如果这个包是项目长期依赖,就不要用临时命令,应该 uv add rich。
同步和锁定依赖
协作项目里,推荐提交 uv.lock。别人拉取项目后运行:uv sync
uv sync 会让 .venv 和项目声明、锁文件保持一致。根据官方 CLI 说明,项目环境不存在时会创建;默认同步会移除未声明的额外包,所以它比「只安装缺的包」更适合复现环境。
常用命令:# 只更新锁文件uv lock# 根据锁文件同步环境uv sync# 同步后运行命令uv run pytest
如果你需要给不使用 uv 的环境提供 requirements.txt,可以导出:uv export --format requirements-txt --output-file requirements.txt
老的 pip-tools 风格也可以用:uv pip compile pyproject.toml -o requirements.txtuv pip sync requirements.txt
选择哪一种,取决于项目边界:
- 项目内部开发:优先
uv.lock+uv sync。 - 部署平台只认
requirements.txt:用uv export或uv pip compile生成。 - 旧项目迁移:先保留
requirements.txt,用uv pip接管安装速度,再慢慢迁移到pyproject.toml。
VS Code 插件
推荐安装这些插件:
PythonPylanceRuffPython DebuggerPython postfix completion
各自职责不同:插件 主要职责 Python 选择解释器、运行测试、调试入口 Pylance 语言服务、跳转、补全、类型检查 Ruff 格式化、导入排序、Lint 诊断和自动修复 Python Debugger 调试 Python 程序 Python postfix completion 后缀补全,提升编辑效率
安装插件后,先在 VS Code 命令面板里选择解释器:Python: Select Interpreter
选择项目里的 .venv。如果没有看到 .venv,先在项目根目录运行:uv sync
VS Code 保存时格式化和修复
在项目级 .vscode/settings.json 中写配置,比写全局配置更可复现。一个基础配置如下:{ "editor.formatOnPaste": true, "editor.formatOnSave": true, "editor.formatOnType": true, "[python]": { "editor.defaultFormatter": "charliermarsh.ruff", "editor.codeActionsOnSave": { "source.fixAll.ruff": "always", "source.organizeImports.ruff": "always" } }}
这段配置的效果是:保存时用 Ruff 格式化、执行可自动修复的 lint、整理 import。
如果项目已经使用 Black,也可以用微软维护的 Black Formatter 插件。但对新项目来说,用 Ruff 同时负责格式化和 lint,配置更少,速度也更一致。
Pylance 配置
Pylance 负责「编辑时理解代码」。它和 Ruff 的边界要分清楚:
- Ruff 更像快速代码质量工具:格式、导入、常见错误、风格规则。
- Pylance 更像语言智能:类型推断、跳转、补全、引用查找。
可以从下面这份配置开始:{ "python.languageServer": "Pylance", "python.analysis.cacheLSPData": true, "python.analysis.autoFormatStrings": true, "python.analysis.autoImportCompletions": true, "python.analysis.completeFunctionParens": true, "python.analysis.typeCheckingMode": "basic", "editor.inlayHints.enabled": "offUnlessPressed", "python.analysis.inlayHints.callArgumentNames": "all", "python.analysis.inlayHints.functionReturnTypes": true, "python.analysis.inlayHints.pytestParameters": true, "python.analysis.inlayHints.variableTypes": true, "python.terminal.activateEnvInCurrentTerminal": true, "python.terminal.shellIntegration.enabled": true}
typeCheckingMode 有几个常用值:值 适合场景 off只想要补全和跳转,不想看到类型报错 basic日常项目推荐起点 strict类型约束较强的项目,或者核心库、长期维护项目
不建议一上来就给所有项目开 strict。严格类型检查很有价值,但它也会暴露很多历史代码问题。如果项目已有大量动态代码,可以先从 basic 开始,把新增模块写干净,再逐步提高标准。
Ruff:格式化和静态检查
Ruff 是 Astral 开发的 Python linter 和 formatter。它的 lint 规则覆盖了 Flake8、isort、pyupgrade 等工具的常用功能,formatter 则可以直接替代 Black。
安装到项目开发依赖后,常用的就四条命令:uv add --dev ruff# lint 检查 / 自动修复uv run ruff check .uv run ruff check . --fix# 格式化 / 只检查格式不改文件(适合 CI)uv run ruff format .uv run ruff format . --check
Ruff 的配置可以放在 pyproject.toml、ruff.toml 或 .ruff.toml。项目已有 pyproject.toml 时,直接集中到里面通常更清爽。[tool.ruff]line-length = 100target-version = "py312"respect-gitignore = trueindent-width = 4[tool.ruff.format]quote-style = "double"indent-style = "space"docstring-code-format = true[tool.ruff.lint]select = [ "E", # pycodestyle error "F", # pyflakes "I", # isort "UP", # pyupgrade "B", # flake8-bugbear "SIM" # flake8-simplify]ignore = []
几点经验:
line-length = 100对现代屏幕比较舒服,团队也常用 88 或 120,关键是统一。target-version应该和项目支持的 Python 版本一致。Ruff 会据此决定能否使用较新的语法改写。select不要一次开太激进。先从E/F/I/UP/B开始,稳定后再扩。- 如果你已经在
[project]里写了requires-python,Ruff 也能从中推断目标版本;显式写target-version时,以 Ruff 配置为准。
pyproject.toml 示例
pyproject.toml 是现代 Python 项目的中心配置文件。它来自 PEP 518 之后的生态演进,用来减少 setup.py、setup.cfg、requirements.txt、各种工具配置文件分散的问题。
一个常见项目可以这样写:[project]name = "vnet"version = "0.1.0"description = "Virtual network playground"readme = "README.md"requires-python = ">=3.12"dependencies = [ "pydantic>=2.10",][dependency-groups]dev = [ "pytest>=8.3", "pytest-cov>=6.0", "ruff",]doc = [ "mkdocs-material>=9.5", "mkdocs-mermaid2-plugin>=1.1",][tool.ruff]line-length = 100target-version = "py312"respect-gitignore = trueindent-width = 4[tool.ruff.format]quote-style = "double"indent-style = "space"docstring-code-format = true[tool.ruff.lint]select = ["E", "F", "I", "UP", "B", "SIM"]ignore = [][tool.pytest.ini_options]addopts = "-ra"testpaths = ["tests"]python_files = ["test_*.py"]python_classes = ["Test*"]python_functions = ["test_*"]log_cli = truelog_cli_format = "%(asctime)s [%(levelname)s] %(message)s"log_cli_date_format = "%m.%d %H:%M:%S"
几个容易踩坑的点:
dependencies放运行时依赖,也就是你的包真正运行需要的库。dev这类开发依赖不要混进运行时依赖,否则部署环境会变重。pythonpath不建议随手写成个人机器绝对路径,例如/home/name/workspace/project。这会让配置只在你的电脑上生效。更推荐采用标准包结构,或者在测试命令中明确设置。indent-style = "tab"虽然 Ruff 支持,但 Python 社区更常见的是 space。除非团队明确要求 tab,否则不建议改。
日常命令清单
下面这组命令基本覆盖个人项目从初始化到检查的流程:# 创建项目uv init my-appcd my-app# 固定 Python 版本uv python pin 3.12uv sync# 添加依赖uv add requestsuv add --dev pytest ruff# 运行uv run python main.pyuv run pytest# 检查和格式化uv run ruff check .uv run ruff check . --fixuv run ruff format .# 查看依赖uv tree# 同步环境uv sync