Ruff v0.0.276 现已发布。可从 PyPI 或您选择的包管理器安装。
pip install --upgrade ruff
友情提醒:Ruff 是一个用 Rust 编写的极速 Python 代码检查工具。 Ruff 可以替代 Flake8(及数十个插件)、isort、pydocstyle、pyupgrade 等工具,同时执行速度比任何单个工具快数十或数百倍。
在 GitHub 上查看完整更新日志,或继续阅读亮点。
Jupyter Notebook 支持 #
Ruff 现已提供对检查 Jupyter Notebooks 的实验性支持。
# Run Ruff over `Notebook.ipynb`.
ruff check Notebook.ipynb
# Re-run Ruff on-save.
ruff check Notebook.ipynb --watch
# Fix any fixable errors in `Notebook.ipynb`.
ruff check Notebook.ipynb --fix
要启用 Jupyter Notebook 文件的代码检查功能,请将 *.ipynb
模式添加到您的 include
设置中,如下所示:
[tool.ruff]
# Allow Ruff to discover `*.ipynb` files.
include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"]
这将提示 Ruff 在任何指定目录中发现 Jupyter Notebook 文件,并相应地对其进行检查。
或者,您也可以直接将 Notebook 文件传递给 ruff
。例如,ruff check /path/to/notebook.ipynb
将始终检查 notebook.ipynb
。
您可以使用 Ruff 的 per-file-ignores
设置在 Jupyter Notebooks 中禁用特定规则。例如,通常允许导入出现在任何单元格中,而不是将其限制在 Notebook 的顶部:
[tool.ruff]
# Allow imports to appear anywhere in Jupyter Notebooks.
per-file-ignores = { "*.ipynb" = ["E402"] }
Jupyter Notebook 支持目前是可选且实验性的,并存在一些已知限制,这些限制将在后续版本中解决(最值得注意的是:不支持混合 Jupyter Magics 和 Python 代码的单元格)。
我们非常希望能得到您的帮助进行测试。有反馈吗?遇到问题了吗?无论是大是小,请随时告知我们。
一流的导入解析 #
Ruff 现在包含一个一流的导入解析器,它基于 Pyright 的导入解析器,支持解析来自命名空间包、类型存根等的导入。
在未来的版本中,该解析器将使我们能够移除各种用户提供的设置(例如 namespace-packages
),解析跨文件引用,检测未使用的依赖项等等。
新规则:unnecessary-list-cast
(PERF101
) #
作用是什么? #
检查对 for 循环可迭代对象进行显式转换为 list
的情况。
为何重要? #
使用 list()
调用来急切地迭代一个已是可迭代的类型(如元组、列表或集合)是低效的,因为它会强制 Python 不必要地创建一个新列表。
移除 list()
调用不会改变代码的行为,但可能会提高性能。
请注意,与所有 perflint 规则一样,这仅作为微观优化,在大多数情况下对性能的影响可以忽略不计。
例如,给定以下代码片段
items = (1, 2, 3)
for i in list(items):
print(i)
请直接使用可迭代对象
items = (1, 2, 3)
for i in items:
print(i)
新规则:try-except-in-loop
(PERF203
) #
作用是什么? #
检查在 for
和 while
循环中使用 try
-except
进行异常处理的情况。
为何重要? #
通过 try
-except
块进行异常处理会产生一些性能开销,无论是否引发异常。
如果可能,请重构代码,将整个循环放入 try
-except
块中,而不是将每次迭代都包装在单独的 try
-except
块中。
此规则仅适用于 Python 3.11 之前的版本,因为 3.11 版本引入了“零成本”异常处理。
请注意,与所有 perflint 规则一样,这仅作为微观优化,在大多数情况下对性能的影响可以忽略不计。
例如,给定以下代码片段
for i in range(10):
try:
print(i * i)
except:
break
请将整个循环移入 try
-except
块中
try:
for i in range(10):
print(i * i)
except:
pass
新规则:manual-list-comprehension
(PERF401
) #
作用是什么? #
检查可以用列表推导式替换的 for
循环。
为何重要? #
当使用 for 循环从现有列表创建转换后的列表时,优先使用列表推导式。列表推导式更具可读性且性能更高。
以下为例,列表推导式在 Python 3.11 上快约 10%,在 Python 3.10 上快约 25%。
请注意,与所有 perflint
规则一样,这仅作为微观优化,在大多数情况下对性能的影响可以忽略不计。
例如,给定以下代码片段
original = list(range(10000))
filtered = []
for i in original:
if i % 2:
filtered.append(i)
请改用
original = list(range(10000))
filtered = [x for x in original if x % 2]
如果您要向现有列表追加元素,请使用 extend
方法
original = list(range(10000))
filtered.extend(x for x in original if x % 2)
新规则:manual-list-copy
(PERF402
) #
作用是什么? #
检查可以用列表复制替换的 for
循环。
为何重要? #
当使用 for 循环创建现有列表的副本时,请优先使用 list
或 list.copy
。直接复制更具可读性且性能更高。
以下为例,基于 list
的复制在 Python 3.11 上快约 2 倍。
请注意,与所有 perflint
规则一样,这仅作为微观优化,在大多数情况下对性能的影响可以忽略不计。
例如,给定以下代码片段
original = list(range(10000))
filtered = []
for i in original:
filtered.append(i)
请改用
original = list(range(10000))
filtered = list(original)
新规则:numpy-deprecated-function
(NPY003
) #
作用是什么? #
检查已废弃的 NumPy 函数的使用情况。
为何重要? #
NumPy 1.25.0 最终确定了先前版本中引入的各种废弃项。
当 NumPy 函数被废弃时,它们通常会被更新、更高效的版本替换,或者被与 NumPy API 其余部分更一致的函数替换。请优先使用较新的 API,而不是已废弃的 API。
例如,给定以下代码片段
import numpy as np
np.alltrue([True, False])
请改用
import numpy as np
np.all([True, False])
新规则:single-string-slots
(PLC0205
) #
作用是什么? #
检查赋值给 __slots__
的单个字符串。
为何重要? #
任何字符串可迭代对象都可以赋值给 __slots__
(最常见的是字符串的 tuple
)。如果将单个字符串赋值给 __slots__
,它将被解释为单个属性名,而不是属性名序列。这可能会导致混淆,因为迭代 __slots__
值的用户可能期望迭代属性序列,但实际上会迭代字符串中的字符。
要在 __slots__
中使用单个字符串属性,请将该字符串包装在可迭代容器类型(例如 tuple
)中。
例如,给定以下代码片段
class Person:
__slots__: str = "name"
def __init__(self, name: str) -> None:
self.name = name
请改将字符串包装在 tuple
中
class Person:
__slots__: tuple[str, ...] = ("name",)
def __init__(self, name: str) -> None:
self.name = name
新规则:complex-if-statement-in-stub
(PYI002
) #
作用是什么? #
检查存根文件中包含复杂条件表达式的 if
语句。
为何重要? #
存根文件支持简单的条件表达式,用于测试 Python 版本和平台差异。然而,类型检查器只理解这些条件表达式的有限子集;复杂的条件表达式可能会导致误报或漏报。
例如,给定以下代码片段
import sys
if (2, 7) < sys.version_info < (3, 5):
...
请改用
import sys
if sys.version_info < (3, 5):
...
新规则:unrecognized-version-info-check
(PYI003
) #
作用是什么? #
检查存根文件中与 sys.version_info
相关的有问题条件。
为何重要? #
存根文件支持使用 sys.version_info
测试 Python 版本差异的简单条件表达式。然而,有一些涉及 sys.version_info
比较的常见错误应避免。例如,与字符串进行比较可能导致意外行为。
例如,给定以下代码片段
import sys
if sys.version_info[0] == "2":
...
请改用
import sys
if sys.version_info[0] == 2:
...
新规则:patch-version-comparison
(PYI004
) #
作用是什么? #
检查存根文件中将 Python 版本与补丁版本(例如 Python 3.8.3)而非主版本和次版本(例如 Python 3.8)进行比较的情况。
为何重要? #
存根文件支持简单的条件表达式,用于测试 Python 版本和平台差异。然而,类型检查器只理解这些条件表达式的有限子集。特别是,类型检查器不支持补丁版本(例如 Python 3.8.3),只支持主版本和次版本(例如 Python 3.8)。因此,存根文件中的版本检查应仅使用主版本和次版本。
例如,给定以下代码片段
import sys
if sys.version_info >= (3, 4, 3):
...
请改用
import sys
if sys.version_info >= (3, 4):
...
新规则:wrong-tuple-length-version-comparison
(PYI005
) #
作用是什么? #
检查将 Python 版本与长度错误的元组进行比较的情况。
为何重要? #
存根文件支持简单的条件表达式,用于测试 Python 版本和平台差异。在与 sys.version_info
进行比较时,避免与长度错误的元组进行比较,这可能导致意外行为。
例如,给定以下代码片段
import sys
if sys.version_info[:2] == (3,):
...
请改用
import sys
if sys.version_info[0] == 3:
...
错误修复 #
- 由 @charliermarsh 在 #5312 中实现:在
mutable-class-default
中支持pydantic.BaseSettings
- 由 @charliermarsh 在 #5314 中实现:允许在
mutable-class-default
中赋值__slots__
- 由 @charliermarsh 在 #5319 中实现:移除 f-string 前缀时避免语法错误
- 由 @charliermarsh 在 #5392 中实现:在
iteration-over-set
中忽略解包 - 由 @dhruvmanila 在 #5383 中实现:在 D407 中将等长等号线替换为破折号线
- 由 @intgr 在 #5405 中实现:从 PYI053 中排除文档字符串
- 由 @charliermarsh 在 #5430 中实现:在阴影上下文中对 E731 使用“手动”修复能力
- 由 @charliermarsh 在 #5467 中实现:检测连续的、非换行符分隔的 NumPy 部分
- 由 @harupy 在 #5478 中实现:修复
unnecessary-encode-utf8
以正确处理括号内字符串的encode
- 由 @charliermarsh 在 #5490 中实现:允许在存根中进行
Final
赋值 - 由 @charliermarsh 在 #5315 中实现:在分类函数类型时尊重
abc
装饰器 - 由 @mayrholu 在 #4903 中实现:允许
flake8-todos
中“缺少作者”规则使用@Author
格式 - 由 @dhruvmanila 在 #5344 中实现:为 RUF013 忽略类型别名
- 由 @hauntsaninja 在 #5352 中实现:将 W605 自动修复更改为尽可能使用原始字符串
- 由 @charliermarsh 在 #5358 中实现:迁移到原始字符串时添加空格
- 由 @charliermarsh 在 #5359 中实现:更新
invalid-escape-sequence
规则 - 由 @charliermarsh 在 #5466 中实现:在 B017 规则中包含 BaseException
- 由 @charliermarsh 在 #5469 中实现:[
flake8-django
] 在DJ012
中跳过重复违规