Chapter 05

ruff format:Black 替代

内置的代码格式化器,99% 兼容 Black,速度快 30 倍,不用再装第二个工具。

5.1 为什么需要 Formatter

Linter 检查"是否有问题",Formatter 则统一"如何排版"——空格、换行、引号、缩进、对齐。关键价值是消除"风格之争":团队里没人再需要争论 '单引号' 还是 "双引号",机器说了算,diff 干净清爽。

Black 是什么?

Black 是 Python 社区事实标准的格式化器,由 Python Core Dev Łukasz Langa 主导。它的口号是 "The uncompromising code formatter"——基本不可配置,强制统一风格。Ruff 的 ruff format 对齐 Black 的这套风格,并用 Rust 重写了一遍。

5.2 基本用法

$ ruff format .            # 格式化当前目录所有 .py
$ ruff format app/         # 限定目录
$ ruff format foo.py       # 单文件
$ ruff format --check .    # 只检查不改(CI 用,非 0 = 有需要格式化的)
$ ruff format --diff .     # 输出会改动的 diff
$ cat foo.py | ruff format -   # 从 stdin 读

5.3 配置项

[tool.ruff.format]
quote-style = "double"                 # 引号:double/single/preserve
indent-style = "space"                 # space/tab
line-ending = "auto"                   # auto/lf/crlf/native/cr
skip-magic-trailing-comma = false
docstring-code-format = true           # 格式化 docstring 里的代码块
docstring-code-line-length = "dynamic"   # 或具体数字

5.4 Black 兼容性

ruff format 与 Black 几乎 1:1 兼容——对同一个合法 Python 文件,两者产生的输出基本一致。Ruff 官方维护了一个兼容性报告,覆盖 CPython 官方测试套件、几十个知名开源项目,绝大多数文件 0 差异。

已知少量刻意的差异(出于性能或设计原因):

不必担心差异

这些差异都不影响代码功能,只是排版细节。从 Black 迁到 ruff format 时,一次性跑 ruff format . 会产生一个小 commit 修正格式——这是正常且一次性的。

5.5 quote-style:引号统一

# quote-style = "double" (默认)
x = "hello"
y = 'contains "double"'   # 含双引号时保留单引号,避免转义

# quote-style = "single"
x = 'hello'

# quote-style = "preserve"
# 不动引号,只管其他排版(迁移期过渡用)

5.6 Magic Trailing Comma

Black/Ruff 的一条重要规则:函数调用/列表/字典的最后一个元素若带尾逗号,强制多行展开。这是开发者表达"我想让它多行"的简单信号。

# 输入(你写的)
result = foo(
    "a",
    "b",
    "c",   # 尾逗号
)

# 输出:保持多行,因为尾逗号告诉格式化器"我要多行"

# 删掉尾逗号后
result = foo("a", "b", "c")   # 单行(如果容得下)

若想关闭这一特性(回到纯机械换行):

[tool.ruff.format]
skip-magic-trailing-comma = true

5.7 Docstring 里的代码块格式化

项目如果喜欢在 docstring 里写 Example: 代码块,启用 docstring-code-format 能让 Ruff 连 docstring 内部的代码也格式化:

def add(a, b):
    """Add two numbers.

    Example:
        >>> add( 1 ,2 )        # 格式化前
        3
        >>> add(1, 2)          # 格式化后
        3
    """
    return a + b

支持 Python doctests(>>> 前缀)和 Markdown 风格代码块(```python)。

5.8 局部禁用格式化

极少数情况下你希望某段代码"不要被格式化"(比如手工排好的大矩阵数据、特殊对齐),用 # fmt: off / # fmt: on

# fmt: off
matrix = [
    [1,  0,  0],
    [0,  1,  0],
    [0,  0,  1],
]
# fmt: on

# 单行版本
x = [1,2,3]  # fmt: skip

5.9 与 Lint 协同

格式化和 Lint 是独立的两步,推荐顺序:先格式化(排版统一后 lint 的噪音变少),再 lint。

# 本地典型流程
$ ruff format .
$ ruff check --fix .

# CI 典型流程
$ ruff format --check .
$ ruff check .
format 与 lint 的"重复"规则

有些规则比如 E501(行太长)在启用 formatter 之后意义不大——formatter 会自动换行,但有些长行(长字符串、长 URL)formatter 也管不了。多数项目会选择 ignore = ["E501"] 完全放手给 formatter。

5.10 小结