Chapter 02

规则体系:F/E/W/B/UP/I…

理解 Ruff 的规则命名和组织方式,知道 800+ 条规则分成哪些"族",以及如何有选择性地启用它们。

2.1 规则的命名规范

Ruff 所有规则都有一个 "前缀字母 + 数字" 的短代码,例如 F401E501B007。这一命名直接沿袭自 Flake8 生态:

前缀字母(prefix)
表示该规则来自哪个"族"(rule family)——也就是对应原来 Flake8 的哪个插件。如 F = Pyflakes,E/W = pycodestyle,B = flake8-bugbear。
数字(code)
该族内该规则的编号,一般 3-4 位。编号在族内唯一,但不同族可能重复(如 E501W501 是两条不同规则)。

选择"整族"的写法也支持:--select F 会启用 F 族所有规则;--select F,E 启用 F 和 E 两族;--select E5 则只启用 E500-E599 之间的规则(前缀匹配)。

2.2 核心规则族一览

前缀来源(原插件)典型用途是否默认启用
FPyflakes真正的 Bug:未定义变量、未使用 import、未使用变量✅ 默认
E/WpycodestylePEP 8 风格:行过长、空白、缩进E 默认,W 部分
Iisortimport 排序与分组❌ 需启用
Npep8-naming命名规范:类名 CamelCase、函数 snake_case❌ 需启用
UPpyupgrade升级过时语法:% → f-string、Listlist❌ 需启用
Bflake8-bugbear易错反模式:可变默认参数、循环闭包❌ 需启用
Sflake8-bandit安全:硬编码密码、evalsubprocess shell=True❌ 需启用
Dpydocstyledocstring 规范:缺失、格式❌ 需启用
C4flake8-comprehensions列表/字典推导式的优化建议❌ 需启用
SIMflake8-simplify简化代码:多余 if/else、冗余 not❌ 需启用
ANNflake8-annotations类型注解完备性❌ 需启用
RUFRuff 原创Ruff 自己新增的规则✅ 部分
PERFPerflint性能反模式❌ 需启用
PLpylintPylint 规则子集❌ 需启用
默认规则 = F + E4/E7/E9

不做任何配置直接跑 ruff check 时,Ruff 默认只开 Pyflakes 全族(F)和 pycodestyle 的一小部分(E4 import、E7 语句、E9 语法)。这保证"开箱即用"不会因风格问题刷屏。想开更多规则要在配置里写 select = [...]

2.3 F 族:Pyflakes(找真 Bug)

F 族是最重要的族,绝大多数是货真价实的 Bug而不是风格问题:

# F401 — 未使用的 import
import json   # 但是下面没用到 json

# F821 — 未定义的变量
print(undefined_name)

# F811 — 重复定义
def foo(): ...
def foo(): ...   # 覆盖了上面那个

# F841 — 赋值后从未使用
def run():
    result = compute()
    return 0   # result 丢了

# F541 — f-string 没有占位符
msg = f"just text"   # f 前缀多余

2.4 E / W 族:pycodestyle(PEP 8 风格)

E = Error(错误),W = Warning(警告)。常用规则:

E501 line-too-long
行超过 line-length(默认 88)。
E711 none-comparison
与 None 比较应用 is None 而不是 == None
E712 true-false-comparison
与 True/False 比较应用 is 或直接真值判断。
E722 bare-except
except: 会吞掉所有异常(包括 KeyboardInterrupt)。
W291 trailing-whitespace
行尾多余空格。
W605 invalid-escape-sequence
字符串里 "\d" 这种不是合法转义的反斜杠——应改用 raw 字符串 r"\d"

2.5 I 族:isort(import 排序)

按 PEP 8 约定,import 要分三组:标准库 → 第三方库 → 本地模块,每组内字母序。Ruff 的 I 规则可自动整理:

# 修复前(I001 会报错)
import requests
import os
from .utils import helper
import json

# ruff check --select I --fix 后
import json
import os

import requests

from .utils import helper

2.6 UP 族:pyupgrade(升级过时语法)

Python 每代都会引入更好的写法,UP 族自动把老代码升级到现代语法:

# UP006 — 使用内置类型而非 typing.List
# 修复前
from typing import List, Dict
def f(xs: List[int]) -> Dict[str, int]: ...
# 修复后(Python 3.9+)
def f(xs: list[int]) -> dict[str, int]: ...

# UP007 — Optional[X] → X | None(Python 3.10+)
# UP008 — super() 代替 super(ClassName, self)
# UP031 — % 字符串格式化 → f-string
name = "world"
msg = "hello %s" % name   # → f"hello {name}"
UP 族依赖 target-version

UP 族生效取决于 target-version(见第 3 章)。比如 UP007(Optional[X]X | None)只有在目标 Python >= 3.10 时才触发,否则会改坏代码。

2.7 B 族:flake8-bugbear(易错反模式)

# B006 — 可变默认参数(Python 最著名的坑)
def bad(items=[]):       # 所有调用共享同一个 list!
    items.append(1)
    return items

# B007 — 循环变量未在循环体中使用
for i in range(10):   # 应改 for _ in range(10)
    do_something()

# B023 — 循环变量被闭包捕获
funcs = [lambda: i for i in range(3)]
# funcs[0]() == funcs[1]() == funcs[2]() == 2,不是 0/1/2

2.8 用 ruff rule 查规则详情

$ ruff rule F401
# 展示该规则的含义、示例、是否可自动修复

$ ruff linter        # 列出所有规则族
$ ruff rule --all    # 列出所有规则(含详细说明)

在线版更直观:docs.astral.sh/ruff/rules——每条规则都有示例代码、Bad/Good 对比、可选配置项。

2.9 选规则的推荐起步集

刚从 Flake8 迁过来的项目,先别一次性开 800 条。建议从"无争议、修复量少"的族开始:

[tool.ruff.lint]
select = [
    "E",   # pycodestyle errors
    "F",   # Pyflakes(真 Bug)
    "I",   # isort
    "B",   # bugbear
    "UP",  # pyupgrade
    "SIM", # simplify
]

跑通之后再考虑 N(命名)、C4(推导式)、RUF(Ruff 原创),以及 S(安全,后端项目尤其推荐)。

2.10 小结