第1章: 环境与工具链
Java 开发者打开 IntelliJ,JDK、Maven、Gradle 一条龙。Python 的世界没有这样一个"大一统"——你需要自己组装工具链。
本章帮你从零搭建一套工业级 Python 开发环境,每个工具都对标你熟悉的 JVM 生态。
1.1 Python 安装与多版本管理 (pyenv)
Java/Kotlin 对比
$ sdk list java
$ sdk install java 21.0.2-tem
$ sdk install java 17.0.9-tem
$ sdk use java 21.0.2-tem
$ sdk default java 21.0.2-tem
$ sdk current java
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
export PATH=$JAVA_HOME/bin:$PATH
Python 实现
pyenv 是 Python 生态中最主流的多版本管理工具,对标 SDKMAN。
brew install pyenv
sudo apt update && sudo apt install -y \
make build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev curl \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
curl https://pyenv.run | bash
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
exec "$SHELL"
pyenv install --list | grep "^\s*3\."
pyenv install 3.12.3
pyenv install 3.11.9
pyenv install 3.10.14
pyenv versions
pyenv global 3.12.3
pyenv local 3.11.9
pyenv shell 3.10.14
pyenv version
pyenv uninstall 3.10.14
pyenv 工作原理(和 SDKMAN 类似):
$ which python
/home/user/.pyenv/shims/python
$ pyenv which python
/home/user/.pyenv/versions/3.12.3/bin/python
pyenv-virtualenv 集成:
brew install pyenv-virtualenv
eval "$(pyenv virtualenv-init -)"
pyenv virtualenv 3.12.3 my-project-env
pyenv activate my-project-env
pyenv deactivate
核心差异
| 维度 | SDKMAN (JVM) | pyenv (Python) |
|---|
| 安装方式 | 下载预编译二进制 | 从源码编译(需要编译依赖) |
| 版本粒度 | JDK 版本 | CPython 版本(还有 PyPy、GraalPy 等) |
| 配置文件 | ~/.sdkman/candidates/java/current | .python-version(目录级) |
| 切换机制 | 修改 PATH + 软链接 | PATH shims 拦截 |
| 项目级锁定 | sdk use java xx(手动) | .python-version(自动,进入目录生效) |
| Windows 支持 | WSL 或直接支持 | pyenv-win(独立项目) |
常见陷阱
何时使用
- 必须用 pyenv: 需要在多个 Python 版本间切换(维护老项目 3.8,新项目 3.12)
- 可以不用: 只用一个 Python 版本,且系统包管理器提供的版本够用(如 macOS
brew install python@3.12)
- 替代方案:
asdf(多语言版本管理器,同时管理 Node.js、Python、Java 等)、conda(自带 Python 版本管理)
1.2 虚拟环境: venv, conda, poetry, uv
Java/Kotlin 对比
关键认知差异: Java/Kotlin 的依赖隔离是"编译期声明 + classpath 组装",Python 的虚拟环境是"运行时环境克隆 + 独立的包安装目录"。Python 的隔离更彻底——连解释器本身都可以不同版本。
Python 实现
venv — 标准库内置(推荐大多数场景)
python3 -m venv .venv
source .venv/bin/activate
which python
python --version
pip install requests
pip list
deactivate
import sys
import os
print(f"Python 路径: {sys.executable}")
print(f"是否在虚拟环境中: {sys.prefix != sys.base_prefix}")
print(f"site-packages: {[p for p in sys.path if 'site-packages' in p]}")
conda — 数据科学生态
brew install --cask miniconda
conda create -n ml-project python=3.11
conda activate ml-project
conda install numpy pandas scikit-learn
pip install requests
conda env export > environment.yml
conda env create -f environment.yml
conda install cudatoolkit=11.8
poetry — 项目管理一体化
curl -sSL https://install.python-poetry.org | python3 -
poetry new my-project
cd my-project
cd existing-project
poetry init
poetry env use 3.12
poetry env list
poetry env remove 3.11
poetry install
poetry run python main.py
poetry run pytest
uv — 2024+ 最快选择
curl -LsSf https://astral.sh/uv/install.sh | sh
uv venv
uv venv .venv --python 3.12
source .venv/bin/activate
uv run python main.py
uv run pytest
核心差异
| 工具 | 速度 | 适合场景 | 依赖管理 | Python 版本管理 |
|---|
| venv | 慢(秒级) | 通用开发 | 需配合 pip | 需配合 pyenv |
| conda | 中等 | 数据科学/ML | conda + pip 混用 | 内置 |
| poetry | 中等 | 库开发/应用 | 内置(lock 文件) | 需配合 pyenv |
| uv | 极快(毫秒级) | 任何场景 | 内置(兼容 pip) | 内置 |
常见陷阱
export PIP_REQUIRE_VIRTUALENV=true
何时使用
| 场景 | 推荐工具 |
|---|
| 通用 Python 开发 | uv 或 venv |
| 数据科学 / ML | conda(管理 CUDA 等非 Python 依赖) |
| 库开发(要发布到 PyPI) | poetry |
| CI/CD 环境 | uv(速度最快) |
| 需要精确锁定依赖 | poetry(poetry.lock)或 uv(uv.lock) |
1.3 包管理: pip vs Poetry vs uv vs pip-tools
Java/Kotlin 对比
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.0.0-jre</version>
</dependency>
</dependencies>
dependencies {
implementation("com.google.guava:guava:33.0.0-jre")
}
关键差异: Maven/Gradle 有传递依赖自动解析和版本冲突解决策略。pip 也有传递依赖解析,但历史上较弱(pip 20.3+ 引入了新的依赖解析器大幅改善)。
Python 实现
pip — 基础包管理器
pip install requests
pip install "django>=4.2,<5.0"
pip install requests==2.32.3
pip install requests~=2.32
pip install --upgrade requests
pip uninstall requests
pip list
pip show requests
pip freeze > requirements.txt
pip install -r requirements.txt
requirements.txt — 传统方式
# requirements.txt — 简单但不够精确
# 直接版本锁定
requests==2.32.3
django>=4.2,<5.0
numpy>=1.24.0
# 带注释的版本
# 生产依赖
flask==3.0.3
gunicorn==22.0.0
# 开发依赖(通常拆分为 requirements-dev.txt)
pytest==8.2.0
ruff==0.4.4
mypy==1.10.1
pip install -r requirements.txt
pip install -r requirements-dev.txt
pyproject.toml + pip-tools — 编译依赖
pip install pip-tools
requests>=2.32.0
django>=4.2,<5.0
pip-compile requirements.in
asgiref==3.8.1
certifi==2024.2.2
charset-normalizer==3.3.2
django==4.2.13
requests==2.32.3
urllib3==2.2.1
pytest>=8.0
mypy>=1.10
-r requirements.in
pip-compile requirements-dev.in -o requirements-dev.txt
Poetry — 现代依赖管理
poetry add requests
poetry add django "^4.2"
poetry add pytest --group dev
poetry add numpy@^1.24
poetry install
poetry install --with dev
poetry install --no-dev
poetry update
poetry update requests
poetry show --tree
[tool.poetry]
name = "my-project"
version = "0.1.0"
description = "My project"
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.10"
requests = "^2.32"
django = "^4.2"
[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
mypy = "^1.10"
ruff = "^0.4"
uv — 2024+ 最快选择
uv pip install requests
uv pip install -r requirements.txt
uv pip compile requirements.in -o requirements.txt
uv pip sync requirements.txt
uv init my-project
cd my-project
uv add requests django
uv add --dev pytest mypy ruff
uv sync
uv run pytest
uv lock
uv tree
[project]
name = "my-project"
version = "0.1.0"
description = "My project"
requires-python = ">=3.10"
dependencies = [
"requests>=2.32.0",
"django>=4.2,<5.0",
]
[dependency-groups]
dev = [
"pytest>=8.0",
"mypy>=1.10",
"ruff>=0.4",
]
核心差异
| 维度 | Maven/Gradle | pip | Poetry | uv |
|---|
| 依赖声明 | pom.xml / build.gradle | requirements.txt | pyproject.toml | pyproject.toml |
| 锁文件 | 没有(依赖解析缓存) | 没有 | poetry.lock | uv.lock |
| 传递依赖解析 | 成熟 | 20.3+ 改善 | 成熟 | 成熟 |
| 版本冲突解决 | 最近优先 / 强制版本 | 第一个匹配 | 自动解决 | 自动解决 |
| 速度 | 慢(JVM 启动) | 慢 | 中等 | 极快(Rust) |
| 依赖范围 | compile/test/provided | 无(需手动拆分) | group | dependency-groups |
常见陷阱
pip install requests
pip install requests==2.32.3
pip install --user requests
何时使用
| 场景 | 推荐 |
|---|
| 快速脚本/学习 | pip + requirements.txt |
| 团队项目/生产环境 | uv 或 poetry(有锁文件) |
| 库开发(发布到 PyPI) | poetry 或 uv |
| CI/CD | uv(速度优势明显) |
| 数据科学 | conda + pip |
| 需要精确控制传递依赖 | pip-tools 或 uv |
1.4 pyproject.toml: 现代项目配置中心 (PEP 621)
Java/Kotlin 对比
<project>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<java.version>21</java.version>
</properties>
<dependencies>...</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
plugins {
java
id("com.diffplug.spotless") version "6.25"
id("org.jlleitschuh.gradle.ktlint") version "12.1"
}
group = "com.example"
version = "1.0.0"
java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }
dependencies { implementation("com.google.guava:guava:33.0.0-jre") }
spotless { kotlin { ktlint() } }
Python 实现
pyproject.toml 是 Python 项目的统一配置文件,取代了历史上散落的 setup.py、setup.cfg、requirements.txt、.flake8、mypy.ini 等文件。
[project]
name = "my-application"
version = "1.0.0"
description = "A modern Python application"
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.10"
authors = [
{name = "Your Name", email = "you@example.com"},
]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"License :: OSI Approved :: MIT License",
]
dependencies = [
"fastapi>=0.110.0",
"uvicorn[standard]>=0.29.0",
"pydantic>=2.6.0",
"httpx>=0.27.0",
"structlog>=24.1.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"pytest-cov>=5.0",
"ruff>=0.4.0",
"mypy>=1.10",
"pre-commit>=3.7",
]
docs = [
"mkdocs>=1.5",
"mkdocs-material>=9.5",
]
ci = [
"tox>=4.14",
"coverage[toml]>=7.5",
]
[project.scripts]
my-app = "my_application.cli:main"
[project.gui-scripts]
my-app-gui = "my_application.gui:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.ruff]
target-version = "py310"
line-length = 120
src = ["src"]
[tool.ruff.lint]
select = [
"E",
"W",
"F",
"I",
"N",
"UP",
"B",
"SIM",
"C4",
"PTH",
]
ignore = ["E501"]
[tool.ruff.lint.isort]
known-first-party = ["my_application"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
[tool.mypy]
python_version = "3.10"
strict = true
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
check_untyped_defs = true
files = ["src", "tests"]
[[tool.mypy.overrides]]
module = ["httpx.*", "uvicorn.*"]
ignore_missing_imports = true
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = [
"-v",
"--tb=short",
"--strict-markers",
]
markers = [
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
"integration: marks integration tests",
]
[tool.coverage.run]
source = ["src"]
branch = true
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"if TYPE_CHECKING:",
"raise NotImplementedError",
"if __name__ == .__main__.:",
]
fail_under = 80
核心差异
| 维度 | pom.xml / build.gradle.kts | pyproject.toml |
|---|
| 标准化 | Maven/Gradle 各自格式 | PEP 621 官方标准 |
| 工具配置 | 在同一个文件中 | 在同一个文件中([tool.xxx]) |
| 构建系统 | Maven/Gradle 内置 | 可选(hatchling, setuptools, flit, poetry-core) |
| 版本管理 | SNAPSHOT / RELEASE | PEP 440 (1.0.0, 1.0.0a1, 1.0.0rc1, 1.0.0.post1) |
| 多模块 | Maven multi-module / Gradle subprojects | workspace(PEP 729, 2024 新增) |
| 仓库声明 | repositories 块 | 不需要(默认 PyPI,可配置 [tool.uv] 等) |
常见陷阱
dependencies = ["requests>=2.32.0"]
[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"
[tool.hatch.version]
source = "vcs"
何时使用
- 所有新项目: 必须用 pyproject.toml(PEP 621 标准,2022+ 生态共识)
- 老项目迁移: 从 setup.py / setup.cfg 迁移到 pyproject.toml
- 不需要 pyproject.toml 的情况: 单文件脚本、Jupyter notebook
1.5 REPL 与 Jupyter Notebook
Java/Kotlin 对比
$ jshell
jshell> System.out.println("Hello")
Hello
jshell> int x = 10;
x ==> 10
jshell> x * 2
$2 ==> 20
$ kotlinc
Welcome to Kotlin version 2.0.0
Type :help for help, :quit for quit
>>> println("Hello")
Hello
>>> val x = 10
>>> x * 2
res1: kotlin.Int = 20
Python 实现
Python REPL 基础
python3
python3.12
>>> 2 ** 100
1267650600228229401496703205376
>>> import json
>>> json.dumps({"name": "Alice", "age": 30}, indent=2)
'{\n "name": "Alice",\n "age": 30\n}'
>>> [x**2 for x in range(10) if x % 2 == 0]
[0, 4, 16, 36, 64]
>>> def fib(n):
... a, b = 0, 1
... for _ in range(n):
... a, b = b, a + b
... return a
...
>>> [fib(i) for i in range(10)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
>>> _
34
>>> __
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
>>> help(str)
>>> help(str.split)
>>> dir(str)
>>> str.__doc__
IPython — 增强版 REPL
pip install ipython
In [1]: %timeit [x**2 for x in range(1000)]
In [2]: %timeit list(map(lambda x: x**2, range(1000)))
In [3]: %timeit sum(range(1000000))
In [4]: !ls -la
In [5]: !pwd
In [6]: files = !ls *.py
In [7]: data = {"name": "Alice", "scores": [90, 85, 92]}
In [8]: data?
In [9]: data??
In [10]: import os
In [11]: os.pa<TAB>
In [12]: %history
In [13]: %hist -g pattern
In [14]: %debug
Jupyter Notebook
pip install jupyter
jupyter notebook
pip install jupyterlab
jupyter lab
import json
from collections import Counter
data = [
{"name": "Alice", "dept": "Engineering", "level": 5},
{"name": "Bob", "dept": "Engineering", "level": 3},
{"name": "Charlie", "dept": "Marketing", "level": 4},
{"name": "Diana", "dept": "Engineering", "level": 7},
{"name": "Eve", "dept": "Marketing", "level": 2},
]
dept_counts = Counter(item["dept"] for item in data)
print(dept_counts)
avg_level = {}
for dept, group in __import__('itertools').groupby(
sorted(data, key=lambda x: x["dept"]),
key=lambda x: x["dept"]
):
levels = [item["level"] for item in group]
avg_level[dept] = sum(levels) / len(levels)
print(avg_level)
核心差异
| 维度 | JShell / Kotlin REPL | Python REPL / IPython / Jupyter |
|---|
| 使用频率 | 极少 | 极频繁(Python 开发的核心工作方式) |
| 第三方库支持 | 困难 | 原生支持 |
| 可视化 | 无 | Jupyter 支持 matplotlib, plotly 等 |
| 代码补全 | 基础 | IPython 强大的 Tab 补全 |
| 调试 | 有限 | IPython %debug, %pdb |
| 分享 | 不方便 | .ipynb 文件可直接分享 |
| 生态 | 几乎没有 | 庞大(数据科学标配) |
常见陷阱
何时使用
| 场景 | 工具 |
|---|
| 快速验证语法/API | Python REPL |
| 性能测试、调试 | IPython(%timeit, %debug) |
| 数据探索、可视化 | Jupyter Notebook |
| 教学、演示 | Jupyter Notebook |
| 日常开发 | VS Code 的交互式窗口(REPL 体验 + 编辑器) |
1.6 IDE 选择: VS Code + Pylance vs PyCharm
Java/Kotlin 对比
JVM 生态的 IDE 选择几乎是确定的:
- Java: IntelliJ IDEA(社区版/旗舰版)
- Kotlin: IntelliJ IDEA(官方推荐)
- Android: Android Studio(基于 IntelliJ)
原因: Java/Kotlin 的类型系统复杂(泛型、继承、重载),需要深度语义分析。
IDE 的代码补全、重构、调试高度依赖类型信息。
Python 实现
Python 生态有两个主流选择,和 Java 生态的"一家独大"不同:
VS Code + Pylance(推荐)
{
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.languageServer": "Pylance",
"python.analysis.typeCheckingMode": "basic",
"python.analysis.autoImportCompletions": true,
"python.analysis.diagnosticSeverityOverrides": {
"reportUnusedImport": "warning",
"reportUnusedVariable": "warning",
"reportMissingTypeStubs": "none"
},
"python.formatting.provider": "none",
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
},
"python.testing.pytestEnabled": true,
"python.testing.pytestArgs": ["tests"],
"editor.rulers": [88, 120],
"editor.tabSize": 4,
"files.associations": {
"*.toml": "toml"
}
}
{
"recommendations": [
"ms-python.python",
"ms-python.vscode-pylance",
"charliermarsh.ruff",
"ms-python.debugpy",
"ms-python.python-test-adapter",
"tamasfe.even-better-toml",
"njpwerner.autodocstring",
"kevinrose.vsc-python-indent",
]
}
PyCharm Professional
PyCharm 的优势:
1. 开箱即用,不需要配置扩展
2. 数据库工具、HTTP 客户端、Docker 支持内置
3. 科学模式(Jupyter 集成、matplotlib 预览)
4. Google App Engine、Django 模板支持
PyCharm 的劣势:
1. 重量级,内存占用大(类似 IntelliJ)
2. 专业版收费($249/年)
3. 插件生态不如 VS Code
4. 对非 Python 语言支持弱
推荐场景:
- 如果你习惯了 IntelliJ IDEA,PyCharm 的体验最接近
- 全栈开发(Python + DB + Docker)
- 公司付费
核心差异
| 维度 | IntelliJ IDEA (JVM) | VS Code + Pylance (Python) | PyCharm (Python) |
|---|
| 定位 | 全功能 IDE | 轻量编辑器 + 扩展 | 全功能 IDE |
| 启动速度 | 慢(5-15s) | 快(1-3s) | 慢(5-15s) |
| 内存占用 | 1-4GB | 200-500MB | 1-3GB |
| 插件生态 | 丰富 | 非常丰富 | 有限 |
| 调试体验 | 优秀 | 良好(debugpy) | 优秀 |
| 重构 | 强大 | 基础 | 良好 |
| 多语言 | Java/Kotlin 为主 | 任何语言 | Python 为主 |
| 价格 | 社区版免费 | 免费 | 社区版免费,专业版收费 |
| AI 辅助 | JetBrains AI | GitHub Copilot | JetBrains AI |
常见陷阱
何时使用
| 场景 | 推荐 |
|---|
| 多语言开发者 | VS Code(一个编辑器搞定所有语言) |
| 从 IntelliJ 转过来的开发者 | PyCharm(体验最接近) |
| 数据科学 | VS Code + Jupyter 扩展 或 JupyterLab |
| 全栈 Python 开发 | PyCharm Professional |
| 追求轻量和速度 | VS Code + Pylance + Ruff |
| 团队标准化 | VS Code(.vscode/ 目录可提交到 git 共享配置) |
1.7 Ruff: Rust 实现的超快 Linter+Formatter
Java/Kotlin 对比
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.3</version>
</plugin>
</plugins>
plugins {
id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
}
Python 实现
Ruff 是一个 Rust 实现的工具,替代了 flake8 + isort + black + pyupgrade 等多个工具,速度快 10-100 倍。
pip install ruff
ruff check .
ruff check src/ tests/
ruff check --fix .
ruff format .
ruff format --check .
ruff rule --all
ruff rule --select E
ruff rule E501
[tool.ruff]
target-version = "py310"
line-length = 120
src = ["src"]
exclude = [
".git",
".venv",
"__pycache__",
"build",
"dist",
"*.egg-info",
]
[tool.ruff.lint]
select = [
"E",
"W",
"F",
"I",
"N",
"UP",
"B",
"SIM",
"C4",
"PTH",
"ERA",
"RUF",
]
ignore = [
"E501",
"B008",
]
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"]
"__init__.py" = ["F401"]
[tool.ruff.lint.isort]
known-first-party = ["my_application"]
section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true
line-ending = "auto"
import os
import json
from pathlib import Path
from collections import OrderedDict
def BadFunctionName():
pass
def get_value(d, key):
if key in d.keys():
return d[key]
return None
config_path = os.path.join("config", "settings.json")
name = "World"
message = "Hello, {}".format(name)
def add_item(item, target=[]):
target.append(item)
return target
$ ruff check demo_ruff.py
demo_ruff.py:1:8: F401 [*] `os` imported but unused
demo_ruff.py:3:33: C408 Unnecessary `OrderedDict` call (rewrite as literal)
demo_ruff.py:7:5: N815 Function name `BadFunctionName` should not use CamelCase
demo_ruff.py:12:19: SIM118 Use `key in d` instead of `key in d.keys()`
demo_ruff.py:18:1: ERA001 Found commented-out code
demo_ruff.py:23:14: UP032 Use f-string instead of `format` call
demo_ruff.py:27:28: B006 Do not use mutable data structures for argument defaults
$ ruff check --fix demo_ruff.py
$ ruff format demo_ruff.py
核心差异
| 维度 | Checkstyle + SpotBugs + ktlint | Ruff |
|---|
| 实现语言 | Java | Rust |
| 速度 | 慢(秒级) | 极快(毫秒级) |
| 工具数量 | 需要 2-3 个工具 | 一个工具替代所有 |
| 规则兼容 | 各自独立 | 兼容 flake8 插件生态 |
| 配置文件 | XML / Kotlin DSL | pyproject.toml |
| 自动修复 | 有限 | 丰富(大部分规则可自动修复) |
| import 排序 | 不涉及 | 内置(isort 兼容) |
常见陷阱
def append_to(element, target=[]):
target.append(element)
return target
def append_to(element, target=None):
if target is None:
target = []
target.append(element)
return target
何时使用
- 所有 Python 项目: Ruff 已经是 2024+ 的事实标准,替代 flake8 + isort + black
- CI/CD: Ruff 的速度优势在 CI 中尤其明显
- pre-commit hook: Ruff 支持作为 pre-commit hook 使用
- 不需要 Ruff 的情况: 团队已经稳定使用 flake8 + black 且没有痛点
1.8 mypy/pyright: 静态类型检查
Java/Kotlin 对比
List<String> names = new ArrayList<>();
names.add("Alice");
public String greet(String name) {
return "Hello, " + name;
}
val names: MutableList<String> = mutableListOf()
names.add("Alice")
fun greet(name: String): String {
return "Hello, $name"
}
关键认知差异: Java/Kotlin 的类型检查是编译器内置的,不可关闭。Python 的类型检查是可选的——通过类型注解 + 外部工具(mypy/pyright)实现。Python 代码没有类型注解也能运行。
Python 实现
mypy — 最成熟的类型检查器
pip install mypy
mypy src/
mypy src/my_module.py
mypy --strict src/
from typing import Optional
def greet(name: str) -> str:
return f"Hello, {name}"
greet("Alice")
def find_user(user_id: int) -> Optional[str]:
"""类似 Kotlin 的 fun findUser(userId: Int): String?"""
if user_id == 1:
return "Alice"
return None
result: str | None = find_user(1)
if result is not None:
print(result.upper())
from collections.abc import Sequence
def process_items(items: Sequence[int]) -> list[str]:
"""Sequence 是只读接口(类似 Kotlin 的 List,Java 的 Iterable)"""
return [str(x) for x in items]
process_items([1, 2, 3])
process_items((1, 2, 3))
def get_config() -> dict[str, int | str]:
return {
"timeout": 30,
"host": "localhost",
"port": 8080,
}
config = get_config()
timeout: int = config["timeout"]
type UserID = int
def lookup(user_id: UserID) -> Optional[str]:
return "Alice" if user_id == 1 else None
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
email: str | None = None
user = User(name="Alice", age=30)
from typing import Any
def risky(x: Any) -> Any:
return x + 1
def safe_add(x: int | str) -> int | str:
if isinstance(x, int):
return x + 1
return x
$ mypy demo_type_check.py
demo_type_check.py:12: error: Argument 1 to "greet" has incompatible type "int"; expected "str"
demo_type_check.py:35: error: Incompatible types in assignment (expression has type "int | str", variable has type "int")
Success: no errors found
pyright — VS Code Pylance 内置
npm install -g pyright
pyright src/
{
"python.analysis.typeCheckingMode": "standard",
"python.analysis.diagnosticSeverityOverrides": {
"reportMissingTypeStubs": "none",
"reportUnusedImport": "warning",
"reportUnusedVariable": "warning",
"reportPrivateUsage": "warning",
}
}
严格模式配置
[tool.mypy]
python_version = "3.10"
strict = true
files = ["src", "tests"]
[[tool.mypy.overrides]]
module = [
"httpx.*",
"uvicorn.*",
"celery.*",
]
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
def add(a: int, b: int) -> int:
return a + b
def get_length(s: str | None) -> int:
if s is None:
return 0
return len(s)
def compare(x: int, y: int | str) -> bool:
return x == y
type Config = dict[str, str | int | bool]
def load_config() -> Config:
return {"host": "localhost", "port": 8080, "debug": True}
核心差异
| 维度 | javac / kotlinc | mypy | pyright |
|---|
| 类型检查时机 | 编译时(强制) | 运行前(可选) | 运行前(可选) |
| 实现语言 | Java / Kotlin | Python | TypeScript |
| 性能 | 快(编译器一部分) | 中等 | 快 |
| 严格模式 | 默认严格 | 需配置 --strict | 需配置 "strict" |
| IDE 集成 | 内置 | VS Code 扩展 | VS Code Pylance 内置 |
| 类型推断 | 强 | 强 | 更强 |
| 协议支持 | 接口 | Protocol | Protocol |
| 渐进式采用 | 不支持 | 支持(逐文件/逐函数) | 支持 |
常见陷阱
def add(a: int, b: int) -> int:
return a + b
add("hello", "world")
from typing import Optional
def get_name() -> Optional[str]:
return None
name = get_name()
print(name.upper())
if name is not None:
print(name.upper())
何时使用
| 场景 | 推荐 |
|---|
| 个人项目/脚本 | 不需要(或只用 pyright basic 模式) |
| 团队项目 | mypy standard 或 pyright standard |
| 库开发(给别人用) | mypy --strict(类型注解是库的 API 契约) |
| 从 Java/Kotlin 转过来 | 建议用严格模式——你习惯了强类型,类型注解能帮你减少错误 |
| 渐进式采用 | 先加返回值类型,再加参数类型,最后启用严格模式 |
本章总结: 工具链速查表
| 需求 | 推荐工具 | 对标 JVM |
|---|
| Python 版本管理 | pyenv | SDKMAN |
| 虚拟环境 | uv / venv | 无直接对应(classpath 隔离) |
| 包管理 | uv / poetry | Maven / Gradle |
| 项目配置 | pyproject.toml | pom.xml / build.gradle.kts |
| 交互式开发 | IPython / Jupyter | JShell(极少用) |
| IDE | VS Code + Pylance | IntelliJ IDEA |
| Lint + Format | Ruff | Checkstyle + ktlint |
| 类型检查 | mypy / pyright | javac 内置 |
推荐的新项目初始化流程(2024+ 最佳实践):
uv init my-project
cd my-project
uv add fastapi uvicorn pydantic
uv add --dev pytest ruff mypy
code .
uv run main.py
uv run pytest
核心认知: Python 的工具链是"组装式"的,不像 JVM 生态的"一站式"。这给了你更大的灵活性,但也需要你主动选择和配置。好消息是,2024+ 的 uv 正在把"组装"变成"开箱即用"。