☰
Ruff N规则集全解析:规则码清单与项目配置实战
2026/10/13 8:34:36 网站建设 项目流程

前几天在Python开发群里,有人抛出一个看似简单的问题:ruff list --select N命令中,规则集/规则码有哪些?底下立刻有人回复“N开头的,什么N801、N802”,也有人反问“N是什么新命令?”。其实这两个答案都不算错,但都没说到点子上。这个问题的关键是先把命令拆明白:ruff list是Ruff提供的“列出规则”子命令,--select N表示只把前缀为N的规则筛选出来。理解了这一点,你就知道问的不是“N命令”,而是“N规则集”。这篇文章我就从这条命令出发,把N规则集的来源、规则码清单、每个规则的实际含义和项目配置方法一次性讲清楚,适合所有用Ruff做代码质量检查的Python开发者和正在纠结规则配置的团队参考。

老实说,问出这个问题并不丢人,因为Ruff的规则体系一开始确实容易让人犯迷糊:E、F、I、UP、B、SIM、PLC、RUF……一长串前缀,乍一看像乱码。但只要弄懂“前缀代表一组规则”这个核心规则,后面所有配置命令都能串起来。我见过不少人是把网络上的Ruff配置片段直接复制进项目,结果被一条N806卡住,不知道去哪查、怎么豁免。这篇文章不会只丢给你一份清单,还会告诉你如何用Ruff自带的命令随时自查,以后再也不用追着别人问“这规则码是什么意思”。

1. 先搞清楚ruff list --select N到底在查什么

1.1 是“列出规则”,不是“启用规则”

这里要重点区分。很多新手第一次看到--select,会误以为它和ruff check --select是一个意思,执行完ruff list --select N之后还去代码里找N开头的报错,结果什么都等不到。原因很简单:list子命令是一个查询动作,它的职责是“把Ruff已知的规则罗列出来”,而--select N只是给这个罗列动作加了一个过滤条件,告诉它“我只要前缀是N的那部分”。真正带检查动作的命令是ruff check --select N .,那个才会在你项目的代码里执行N规则并输出违规报告。

所以这条命令的正确心智模型是“书架查询”而不是“书架整理员”。你问的是“架子上有哪些N类书”,不是“把N类书都拿出来整理一遍”。搞清楚这个区别之后,你后续再做配置就不会把list命令写进CI脚本当成检查步骤了。我自己就在CI里踩过这个误区,一开始想用ruff list --select N做门禁,后来才发现它根本不扫描项目文件,只输出规则清单。

1.2--select N的筛选逻辑和输出长什么样

在终端里直接运行:

ruff list --select N

某版本下的输出大致如下(版本不同,条目或字段会有细微差异,以你自己终端为准):

N801 invalid-class-name N802 invalid-function-name N803 invalid-argument-name N804 invalid-first-argument-name-for-class-method N805 invalid-first-argument-name-for-method N806 non-lowercase-variable-in-function N807 dunder-function-name N811 constant-imported-as-non-constant N812 lowercase-imported-as-non-lowercase N813 camelcase-imported-as-acronym N814 camelcase-imported-as-constant N815 mixed-case-variable-in-class-scope N816 mixed-case-variable-in-global-scope N817 camelcase-in-acronym N818 error-suffix-on-exception N999 invalid-module-name

这里面第一列是规则码,第二列是这条规则的速记名。规则码的编号区间不代表严重程度,只是插件内部的历史编号习惯,N801到N819是命名基础规则,N999则是一个特殊规则,专门管模块文件名。如果你只想看其中某一段编号,还可以配合管道过滤,比如:

ruff list --select N | grep N8

这样输出会缩小到N80x和N81x这些常用规则,省得每次在一长串里找。

1.3 用ruff rule看单条规则的完整“说明书”

清单只是目录,真正的细节要看ruff rule。比如你看到N803,想知道它到底判断什么、有没有自动修复选项,直接执行:

ruff rule N803

Ruff会把这条规则的完整描述、违规示例、合规示例、是否支持fix都打印出来。这个命令比网上翻二手资料可靠得多,尤其当你用的Ruff版本比较新时,官方文档不一定第一时间更新,但rule命令输出的一定是你本地版本的真实行为。另一个值得记的命令是ruff linter,它会输出一份“所有内置规则来源”的汇总表,能看到每个前缀对应哪个底层插件。

所以回到标题里的问题,与其等别人给你背一张可能过时的表,不如把这两条命令当成你的随身工具。先ruff list查目录,再ruff rule查详情,基本能解决90%的“这个规则码什么意思”类问题。

2. N规则集的来源与完整速查清单

2.1 规则码前缀 = 规则的“籍贯”

Ruff的规则码不是随机字母,每个前缀都对应一个规则来源。这有点像身份证号的前几位代表户籍地:看到E,你该想到pycodestyle,它管PEP8格式问题;看到F,你该想到Pyflakes,它管未使用导入、未定义变量这类逻辑缺陷;看到I,你该想到isort,它管导入排序。Ruff把很多Python lint工具集成进了同一个二进制,同时保留了原插件的前缀编码,所以从flake8迁移过来的项目几乎不用重新学习规则编码。

这套前缀体系的另一个好处是“可组合”。你在配置里写select = ["N"],Ruff就知道要启用整组命名规则;写select = ["E", "N"],就是同时启用格式规则和命名规则。前缀编码实际成了批量操作最基本的单位,这也是为什么ruff list --select N这种命令会存在:Ruff官方知道,用户需要按组去查规则,而不是一条条代码去背。

2.2 N = pep8-naming,不是“新规则”

N前缀继承自一个历史悠久的flake8插件:pep8-naming。这个插件专门检查PEP8里的命名约定,包括类名是否用大驼峰、函数名和变量名是否用蛇形小写、classmethod的第一个参数是否叫cls、实例方法的第一个参数是否叫self、自定义异常是否以Error结尾等。Ruff把它原生实现了一遍,规则码仍以N开头,所以在Ruff里看到N开头的错误码,基本就等于告诉你“命名规范出问题了”。

命名规则比较特殊,它们通常不会导致程序跑错,但直接影响代码可读性和团队协作效率。比如一个把userName和user_name混用的项目,阅读成本会肉眼可见地上升。很多团队早期不重视命名,等到项目大了再改,成本非常高。Ruff把N规则集内置,就是希望你在代码入库前就发现这些问题,而不是靠人工review去翻。

2.3 完整N规则码速查表

我按主流Ruff版本的清单整理了一份速查表,方便你对照使用:

规则码速记名检查对象
N801invalid-class-name类名是否使用大驼峰CapWords
N802invalid-function-name函数名是否使用蛇形小写
N803invalid-argument-name函数参数名是否使用蛇形小写
N804invalid-first-argument-name-for-class-methodclassmethod第一个参数是否命名为cls
N805invalid-first-argument-name-for-method实例方法第一个参数是否命名为self
N806non-lowercase-variable-in-function函数内部变量是否使用小写
N807dunder-function-name双下划线特殊方法名的书写约定
N811constant-imported-as-non-constant导入常量时别名是否保持全大写
N812lowercase-imported-as-non-lowercase导入小写名称时别名是否保持小写
N813camelcase-imported-as-acronym导入驼峰名称时别名是否改成了缩写式
N814camelcase-imported-as-constant导入驼峰名称时别名是否改成了常量式全大写
N815mixed-case-variable-in-class-scope类作用域变量是否使用混合大小写
N816mixed-case-variable-in-global-scope模块级变量是否使用混合大小写
N817camelcase-in-acronym驼峰命名中的缩略词是否过度大写
N818error-suffix-on-exception自定义异常类名是否以Error结尾
N999invalid-module-name模块文件名是否使用蛇形小写

这些规则有一个很明显的特点:几乎全部涉及“名字该怎么起”,而不是“代码该怎么跑”。所以N组规则和大多数逻辑类规则不冲突,可以和F、E、I、UP等规则无缝混用。

2.4 版本在变,清单要自己会刷新

Ruff的迭代速度很快,规则集不是固定不变的。某个大版本可能新增一条N开头的规则,也可能把某条旧规则调整到preview状态,甚至修改速记名。所以最稳的做法不是记住上面这张表,而是记住“查询方法”:升级Ruff之后,第一时间跑一遍ruff list --select N,看有没有新面孔。

我自己就有过一次印象深刻的经历:某次升级后,CI里突然多了一批N818报错,第一反应是想加ignore。后来用ruff rule N818查了一下,发现团队里有人写了class TimeoutFailure(Exception),确实该改成TimeoutError这类命名。这条新规则等于替我们把命名债找了出来。遇到新增规则先别急着忽略,看一眼它想提醒你什么,通常都是项目里真实存在的问题。

3. 常用N规则码逐个拆解:违规写法与合法写法

3.1 N801 / N802 / N803:类名、函数名、参数名的基本盘

这三条是N组里出现频率最高的规则,也是PEP8命名约定最基础的部分。先看类名:

# N801 违规:类名应该使用大驼峰 class my_service: pass # 合规 class MyService: pass

再看函数名:

# N802 违规:函数名应该使用蛇形小写 def getData(): return 1 # 合规 def get_data(): return 1

参数名也一样:

# N803 违规:参数名应该使用蛇形小写 def merge(firstName, lastName): return f"{firstName} {lastName}" # 合规 def merge(first_name, last_name): return f"{first_name} {last_name}"

这三条在真实项目里最容易扎堆出现。尤其是从Java或前端转过来的代码风格,大量getUserName、requestParam这类命名;还有直接从接口文档“原样搬”的参数名,往往带大写。遇到这种情况不要慌,先看Ruff报的是哪条规则码,再用IDE的重命名功能批量处理。Ruff对这三条规则一般不提供自动修复,原因是改函数名或参数名可能牵涉到所有调用点,贸然自动改不安全,这也是它“宁可提示,不可乱改”的设计思路。

3.2 N804 / N805:self 和 cls 不能写反

类方法和实例方法的第一参数命名,是Python新手最容易忽略的约定。看下面这个例子:

class Service: @classmethod def create(self): # N804 违规:classmethod第一个参数应叫cls return self() @classmethod def create(cls): # 合规 return cls()

普通实例方法正好相反:

class Service: def hello(cls): # N805 违规:实例方法第一个参数应叫self print("hello") def hello(self): # 合规 print("hello")

Ruff并不会因为参数叫别的名字就报语法错误,Python本身也不强制这两个参数名,但约定俗成的语义是:cls指向类本身,self指向实例。如果写反或者乱写,读代码的人很容易误解方法内部拿到的到底是什么。N804和N805经常一起出现在同一个类里,改的时候最好把整个类的参数名统一过一遍。记忆技巧其实很简单:classmethod对应cls,普通方法对应self,先记classmethod,再记普通方法。

3.3 N806 / N815 / N816:变量名的小写纪律

这三条规则覆盖不同作用域的变量命名。函数内部变量不能混合大小写:

def process(): Total = 0 # N806 违规:函数内变量应小写 total = 0 # 合规

类作用域的变量,即使看起来像“配置项”,也不应该混合大小写。很多人在这里有误解,以为CacheTimeout = 60放在类里很合理,实际上Ruff会报N815:

class Settings: CacheTimeout = 60 # N815 违规:类作用域变量不应混合大小写 cache_timeout = 60 # 合规

模块级变量同理,MaxThreadCount = 4这种写法会触发N816:

MaxThreadCount = 4 # N816 违规:全局作用域变量不应混合大小写 max_thread_count = 4 # 合规 MAX_THREAD_COUNT = 4 # 合规:全大写的模块级常量不算mixed case

这里有个容易踩的坑:N816只盯“混合大小写”,全大写的常量并不会被它拦下来。很多人一看N816报错,下意识把变量改成全大写,比如把MaxThreadCount改成MAX_THREAD_COUNT,对于常量语义的全局变量确实是合理做法;但如果它只是一个会变化的状态变量,改成全大写反而误导别人。正确做法是先分清这个变量的角色是常量还是普通变量,再决定用MAX_THREAD_COUNT还是max_thread_count。

3.4 N811-N818:导入别名与异常类命名的细节

导入别名也是命名规范的一部分。你有没有见过这种代码:

from typing import TYPE_CHECKING as t_checking # N811 违规:常量别名应为全大写 from typing import cast as Cast # N812 违规:小写名称的别名不应改成大写

导入别名的本质是给外部名字换一个“本地称呼”,这个称呼同样要遵守PEP8命名约定。全大写常量被导入成小写,或者小写函数被导入成大写,都会让人在后续阅读时搞不清它的真实身份。N813和N814则是处理另一种情况:把驼峰风格的名字导入成缩写式或常量式,同样会被拦截。项目里如果大量使用import X as Y来缩短名称,这几条规则尤其值得开。

异常类的命名约束则更贴近语义。自定义异常如果不以Error结尾,别人在except子句里看到时,第一眼很难判断这是不是一个异常类型:

class TimeoutFailure(Exception): # N818 违规:异常类名应以Error结尾 pass class TimeoutError(Exception): # 合规 pass

还有一个容易忽略的N817,它管的是“缩略词在驼峰命名里不要全大写”。比如HTTPServerError会触发N817,因为这相当于HTTP + ServerError,而PEP8风格更倾向HttpServerError,缩略词只保留首字母大写。这条规则在大项目里争议比较大,因为团队可能习惯保留全大写缩写。如果团队内部文档或历史代码已经统一了全大写缩写风格,可以直接ignore N817,这种取舍本身也是规则配置的常态。

3.5 N999:模块文件名也是PEP8的管辖范围

N999是N组里比较特殊的一条,它检查的是模块文件本身的名字。假设你有一个文件叫fooBar.py,Ruff会报invalid-module-name,正确的写法是foo_bar.py。这条规则在模块导入时尤其重要,因为import fooBar和import foo_bar在Python里是两个完全不同的模块名,改名会直接影响所有导入语句。

老项目重构时碰N999要特别小心。改一个文件名不难,难的是同步更新项目里的所有import fooBar和相对导入。比较稳妥的做法是先用ruff check --select N999 .把所有问题文件列出来,再逐个文件重命名并搜索导入引用。如果某些目录是自动生成的代码或第三方迁移产物,可以直接放进per-file-ignores,不一定要手工改。

4. 实战配置:让N规则集真正落地到项目里

4.1 最小可用配置

在pyproject.toml里启用N组规则,代码非常少:

[tool.ruff.lint] select = ["E", "F", "I", "N"]

这里有个关键点:select = ["N"]是整组启用,不需要把N801、N802一条条列出来。Ruff收到前缀N,会自动展开成当前版本下所有N开头的规则。对新项目,我通常建议从["E", "F", "I", "N"]起步,这四个组已经覆盖了格式、未使用代码、导入排序、命名规范四大基础项。等项目稳定后,再往上加UP、B这类更进一层的前缀组。

4.2 误报治理三板斧:ignore、per-file-ignores、noqa

N规则不是每条都适合所有代码。比较典型的场景是测试文件:测试辅助函数经常故意用简短的非标准命名,比如setup_db()这类没问题,但如果有特殊约定,就会报N802。Django项目里,class Meta:内部的属性很多是混合大小写,比如verbose_name还算正常,但某些字段约束名很容易触发N815。遇到这种情况,正确做法是分层豁免。

第一层是全局精确忽略:

[tool.ruff.lint] ignore = ["N806"]

第二层是按文件忽略,这个最常用:

[tool.ruff.lint.per-file-ignores] "tests/**/*.py" = ["N802", "N803", "N806"] "**/migrations/**/*.py" = ["N999"]

第三层是行内忽略,只在确实需要豁免的单行后面加注释:

value = get_User_Result() # noqa: N802

我的建议是:能按文件豁免就不要全局豁免,能局部豁免就不要按文件豁免。因为全局ignore会掩盖大量真实问题,范围越小越安全。尤其是测试目录,它命名风格跟业务代码不同很正常,直接给per-file-ignores即可,不要让N规则在测试目录里刷屏。

4.3 老项目怎么渐进式启用N规则

老项目直接全量开N规则,报错数量往往大得吓人,这时候硬改会让团队很痛苦。我推荐渐进式推进方案。第一步先看总量:

ruff check --select N . --statistics

这条命令会按规则码统计违规数量,比如N802有30处、N803有12处、N815有5处。根据统计结果,从数量最少、或者改动影响最小的规则开始清理。比如先处理N999,改几个文件名,再处理N815,修几处类属性,最后集中处理N802这种大头的函数命名。N组的规则大多数没有自动修复,所以本质上是一场“手动改名”工程。不要用--fix期待它自动改,真正的效率提升来自IDE的重命名功能:安装Ruff的编辑器扩展后,把光标放到违规名称上按重命名快捷键,引用的地方会一起更新,远比手工搜索替换安全。

渐进式推进的另一个技巧是“先新增目录,再存量代码”。新代码从第一天就启用N规则,老代码用per-file-ignores暂时豁免,然后每个迭代都清理一部分存量问题,直到最后把豁免范围收掉。这样不会出现一个大PR改几百个文件的情况,review负担小,回归风险也低。

4.4 N规则与格式化的分工:别指望它替你改名

很多人会把Ruff的理解简化成“linter加格式化”,其实ruff check和ruff format是两条完全不同的职责线。ruff format只管空白、缩进、引号、换行这些格式化问题,它不管命名;N规则管的是语义层面的名字,两者互不替代。你在配置里同时开select = ["N"]和启用ruff format,得到的是一套“格式+命名”双通道质量检查。

N规则大多没有autofix,这不是Ruff能力不行,而是命名改动本身有风险。函数改名叫get_data之后,所有调用它的地方都得同步改;类改名叫MyService之后,实例化、继承、导入全都要变。自动格式化工具可以安全地改缩进和引号,但安全地改名字需要引用分析,Ruff目前选择的是“提示人工处理”,这也是一种保守稳妥的设计。所以我在团队里反复强调:看到N报错,把它当成一条待办,而不是一个可以一键消掉的红色警告。

5. 从N出发:Ruff规则前缀体系与批量筛选技巧

5.1 常见前缀速查:E/F/I/UP/B这些代码都来自哪

理解了N的来历,其他前缀也就不难懂了。我整理了一张覆盖日常最常碰到的前缀表:

前缀来源/底层工具检查重点
E / WpycodestylePEP8格式问题,如E501行过长、W291行尾空格
FPyflakes未使用导入、未定义名称等逻辑缺陷
Iisort导入排序与导入分组
Npep8-naming命名规范
UPpyupgrade老语法升级,如typing.List改写为list
Bflake8-bugbear常见bug模式,如B006可变默认参数
Aflake8-builtins变量名覆盖Python内置函数
Sflake8-bandit安全问题,如S102误用exec
C4flake8-comprehensions建议用推导式简化列表/字典构造
C90mccabe圈复杂度超阈值
SIMflake8-simplify简化冗余表达式和分支
ARGflake8-unused-arguments未使用的函数参数
PTHflake8-use-pathlib建议用pathlib替代os.path
ERAeradicate扫描注释掉的代码
PDpandas-vetpandas API使用中的常见问题
PLC/PLE/PLW/PLRpylintpylint四类规则:约定/错误/警告/重构
RUFRuff原生规则Ruff自定义规则,无flake8对应版本
ANNflake8-annotations缺失类型注解的问题
PTflake8-pytest-stylepytest测试编写风格
RETflake8-returnreturn语句风格问题
SLFflake8-self实例外部访问私有成员
T10 / T20flake8-debugger / flake8-print调试器断点残留 / 调试用print
Dpydocstyle文档字符串格式
Qflake8-quotes引号风格一致性

这张表不需要背,目的是建立“前缀=来源=大致检查方向”的直觉。看到B006,你知道它来自bugbear,大概率是可变默认参数;看到S102,你知道它来自bandit,多半和安全有关;看到RUF开头的规则,你知道这是Ruff特有的,需要单独看文档。有了这个直觉,处理报错的效率会高很多。

5.2 用前缀批量圈定检查范围

前缀体系最大的价值是可组合可批量。ruff list支持一次筛选多个前缀,比如:

ruff list --select N,UP

这会同时列出N组命名规则和UP组语法升级规则,适合你在评估“项目要不要把这两组都开了”时快速看全貌。检查命令同样支持前缀组合:

ruff check --select N,UP,B .

一次性把命名、语法升级、常见bug三类问题都查出来。想在清单里单独看某一段编号,也可以继续用grep这类常规命令行工具。前缀批量筛选的思路不仅适用于N,也适用于所有规则组。比如临时排查安全问题时,直接ruff check --select S .,把安全相关规则单独跑一遍;想评估复杂度时就跑ruff check --select C90 .,完全不用改配置文件。

5.3 保持对新规则的敏感度:让Ruff自己告诉你答案

最后再说一个习惯:每次Ruff升级后,我做的第一件事是跑ruff linter和ruff list --select N,检查内置规则来源和N组规则有没有变化。新版本很可能引入一条你完全没听过的新规则,而它针对的恰恰是你项目里长期存在的坏味道。网络上的配置模板会过时,别人的博客会过时,但Ruff命令输出的是你自己本地版本的真实清单。

遇到任何一条不认识的规则码,无论它的前缀是什么,都可以用ruff rule <规则码>查看详情。这条命令会给出完整的触发条件和示例,比任何二手解释都准确。把ruff list和ruff rule变成习惯之后,你对规则码的恐惧会消失,剩下的只是“这条规则适不适合我的项目”的判断题。

最后分享一个小习惯。我每次拉一个新项目,先做的事就是跑一遍ruff list --select N,把这组规则清单过一眼;等Ruff发新版,再跑一次,专门看N组有没有多规则。曾经有一次升级后突然冒出来一批N818报错,一开始觉得很烦,后来发现是团队里有人写的自定义异常没带Error后缀,确实该改。所以遇到新的N规则先别急着ignore,看一眼它想提醒你什么,很多时候是项目里真实存在的命名债。也别把N规则当教条——测试、迁移脚本该豁免就豁免,per-file-ignores就是给你留的口子。记住“N = naming”,下次再看到N开头的错误码,你就知道它在跟你聊“名字”这件事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询