Chapter 01

认识 Ruff 与安装

从零理解 Ruff 的定位,弄清它和 Flake8/Black/pylint 的关系,然后在 30 秒内完成安装并跑出第一份报告。

1.1 Ruff 是什么?

Ruff 是由 Astral 团队(即 uv 的作者)用 Rust 编写的一款 Python 静态代码质量工具。它同时扮演两种角色:

Linter(代码检查器)
静态分析 Python 源代码,找出风格问题(如行太长)、潜在 Bug(如未定义变量)、反模式(如用可变默认参数)等,并给出建议。对应命令:ruff check
Formatter(代码格式化器)
按统一规则重写代码的排版:缩进、引号、换行、参数对齐等。99% 兼容 Black。对应命令:ruff format

一句话概括:Ruff = Flake8 + Black + isort + pyupgrade + pydocstyle + bandit + ... 全部合在一个 Rust 二进制里,并且速度快 10-100 倍。

什么是静态分析?

静态分析(Static Analysis)指"不运行代码,只看源码文本/AST"就能发现问题的技术。与之对应的是动态分析(运行时检查),如 pytest 测试覆盖率、运行时 profiler。Ruff、mypy、pylint 都属于静态分析。

1.2 与 Flake8 / Black / pylint 的对比

工具语言职责速度(万行代码)当前定位
RuffRustLint + Format + 修复< 1 秒新一代默认选择
Flake8PythonLint(含插件)20-60 秒被 Ruff 替代中
BlackPythonFormat5-15 秒ruff format 替代中
isortPython排 import3-10 秒被 Ruff 规则 I 族替代
pylintPython深度 Lint + 类型推断60-300 秒仍有独特价值(类型推断)
mypyPython类型检查30-120 秒与 Ruff 互补使用
什么时候还需要 mypy / pylint?

Ruff 侧重"语法/风格/局部模式"的快速扫描,不做全局类型推断。若需要严格类型检查(例如 x: int = "abc" 的跨函数类型错误),仍要搭配 mypypyright。Astral 正在开发的 ty 项目(Rust 写的类型检查器)目标是未来替代 mypy。

1.3 为什么这么快?

Ruff 之所以快 10-100 倍,核心有三点:

  1. Rust 的零成本抽象 + 无 GC:AST 遍历、字符串处理、哈希表查询比 Python 原生快一个数量级。
  2. 并行文件处理:用 rayon 多线程并行扫描所有 Python 文件,充分利用多核 CPU。
  3. 单次 AST 遍历执行所有规则:Flake8 每个插件都要各自遍历一次 AST;Ruff 把所有启用的规则在一次遍历中同时跑完。
AST 是什么?

AST(Abstract Syntax Tree,抽象语法树)是源代码被解析后形成的树状结构,每个节点对应一个语法成分(函数定义、变量赋值、表达式等)。静态分析工具不是直接扫"文本",而是扫 AST,这样才能区分"字符串 "def foo"" 和"真的函数定义 def foo()"。

1.4 安装 Ruff

Ruff 是单个静态链接的二进制,安装极简。按你已有的工具选择一种即可:

方式 A:uv 安装(推荐)

# 作为项目依赖
$ uv add --dev ruff

# 或全局工具(类似 pipx)
$ uv tool install ruff

# 免安装、一次性运行
$ uvx ruff check .

方式 B:pipx(全局隔离)

$ pipx install ruff
$ ruff --version
# ruff 0.11.x

方式 C:pip(项目 venv 内)

$ pip install ruff

方式 D:Homebrew(macOS)

$ brew install ruff
不要用系统 Python 的 pip 全局安装

macOS/Linux 的系统 Python 是只读的。用 pipxuv tool install 保证隔离。Windows 用户可直接 pip install ruff 到用户目录。

1.5 第一次运行:ruff check

新建一个 demo.py

# demo.py
import os, sys        # 多个 import 同一行
import json

def greet(name="world",):
    msg = f"hello {name}"
    print(msg)
    return 0

unused = "never read"   # 未使用变量
print(json.dumps({"a":1}))

运行:

$ ruff check demo.py
demo.py:2:1: E401 Multiple imports on one line
demo.py:2:8: F401 [*] `os` imported but unused
demo.py:2:12: F401 [*] `sys` imported but unused
demo.py:11:1: F841 [*] Local variable `unused` is assigned to but never used
Found 4 errors.
[*] 3 fixable with the `--fix` option.

每一行的含义:

demo.py:2:1
文件路径:行号:列号,编辑器点击可直接跳转。
E401 / F401 / F841
规则代码。字母是规则族(E=pycodestyle errors,F=Pyflakes),数字是该族下的具体规则。在第 2 章我们会完整介绍。
[*]
表示该问题可以被 ruff check --fix 自动修复。

1.6 第一次运行:ruff format

$ ruff format demo.py
# 1 file reformatted

执行后 demo.py 的排版会被重写:字符串统一成双引号、多余尾逗号去掉、语句间空行规范。Ruff format 默认与 Black 风格几乎一致,无需额外配置就能得到公认的美观排版。

只看不改:--check 模式

在 CI 中通常只需要"检查格式是否正确,不实际写入文件",用 ruff format --check。若格式不一致返回非 0 退出码,让 CI 失败。

1.7 常用命令速查

$ ruff check .                    # 检查当前目录
$ ruff check --fix .              # 检查并自动修复
$ ruff check --watch .            # 文件变化时重新检查
$ ruff format .                   # 格式化
$ ruff format --check .           # 仅检查是否需要格式化(CI 用)
$ ruff --version                  # 查看版本
$ ruff check --statistics .       # 规则触发次数统计
$ ruff check --select E,F .       # 只跑 E、F 规则族

1.8 小结与下一步

下一章我们将深入 Ruff 的规则体系:F/E/W/B/UP/I 这些前缀分别代表什么,为什么 Ruff 能复刻 Flake8 几十个插件。