发布于 2026-01-06 0 阅读
0

Bye-bye pylint Introduction Pain points Turning the page Categories The rise of the linters What’s next wemake-python-styleguide

再见,pylint

介绍

痛点

翻页

类别

短切工的兴起

接下来会发生什么?

wemake-python-styleguide

原文发表于我的博客

我使用pylint已经将近十年了。

十年后,我决定不再使用它了。

原因如下。

介绍

我们先来看一个例子。请看下面这段显然是错误的代码:

def foo():
    ...

if __name__ == " __main__"
    foo(1, 2, 3)
Enter fullscreen mode Exit fullscreen mode

以下是运行 pylint 后可能的输出结果:

$ pylint foo.py
foo.py:4: [E1121(too-many-function-args),]
  Too many positional arguments for function call

Enter fullscreen mode Exit fullscreen mode

现在让我们来看看我在使用 pylint 时遇到的一些问题。

痛点

初始设置

pylint 的初始配置总是有点麻烦。不过,只要遵循一些建议,就能顺利完成。

假阴性

pylint 的一个常见问题是误报率过高。也就是说,pylint 会认为某些代码有问题,但实际上代码完全没问题。

例如,每当我的类主要包含数据时,我都喜欢使用attrs库,就像这样:

import attr

@attr.s
class Foo:
    bar = attr.ib()
    baz = attr.ib()
Enter fullscreen mode Exit fullscreen mode

这几行代码就给了我一个易于阅读的程序__repr__、一套完整的比较方法、合理的构造函数(以及其他一些功能),而且没有任何样板代码。

但是当我运行pylint这个文件时,却得到:

foo.py:3: [R0903(too-few-public-methods), Foo] Too few public methods (0/2)
Enter fullscreen mode Exit fullscreen mode

当然,要求每个类至少有两个公共方法是完全合理的。大多数情况下,如果一个类只有一个公共方法,最好直接使用函数,像这样:

# What you wrote:
class Greeter
    def __init__ (self, name="world"):
        self._name = name

    def greet(self):
    print("Hello", self.name)

# What you should have written instead:
def greet(name="world"):
    print("Hello" , name)
Enter fullscreen mode Exit fullscreen mode

但是,pylint 并不知道所有“动态”添加的优秀方法,因此attr错误地认为我们的设计是错误的。

因此,如果在持续集成 (CI) 过程中运行 pylint,并且发现任何错误导致构建失败,则必须插入特殊格式的注释以在本地禁用此警告:

import attr

# pylint: disable=too-few-public-methods
@attr.s
class Foo:
  ...
Enter fullscreen mode Exit fullscreen mode

这样做很快就会让人厌烦,尤其是在每次升级 pylint 时都会添加一大堆新的检查项。有时它们会发现代码中的新问题,但你仍然需要逐个检查每个新错误,以确定它是误报还是真正的问题。

但到目前为止,我已经成功克服了这些痛点。那么,是什么改变了这一切呢?

翻页

发生了两件事:

首先,我开始使用 mypy和一个“真正的”类型系统1

我发现 mypy 可以捕获 pylint 可以捕获的许多错误,而且可能更多。

此外,由于 mypy 使用类型注解,因此它比 pylint 更快、更精确(因为它不需要“猜测”任何事情)。

最后但同样重要的是,mypy 的设计理念是逐步使用,只有在确定出现问题时才会发出错误。

其次,我决定将我的一个项目移植到 Python 3.7。我不得不将 pylint 从 1.9 升级到 2.1(因为旧版本的 pylint 不支持 Python 3.7),结果出现了 18 个新的 pylint 错误,其中只有一个是真正相关的。

就在这时,我决定后退一步。

类别

正如我们在这些示例中所看到的,pylint 错误消息包含错误的简短名称(例如too-many-function-args),以及以字母为前缀的数字 ID(E1121)。

每个字母对应一个 pylint类别

以下是完整列表:

  • F)atal(某些因素阻止了pylint正常运行)
  • 错误)严重错误
  • 警告)(并非严重问题)
  • )信息(例如无法解析# pylint: disable注释之类的错误)
  • C)惯例(编码风格)
  • 重构) (可以用更清晰或更符合 Python 风格的方式编写的代码)

请注意,只有当我们试图了解 pylint 为什么没有按预期运行时,FatalInfo类别才有用。

短切工的兴起

我意识到,对于几乎所有 pylint 类别,我都可以使用其他代码检查工具(不仅仅是 mypy)。

  • 一些错误信息也可以被pyflakes捕获,它速度很快,而且误报率也很低。
  • pycodestyle可以处理约定类别
  • mccabe还可以捕获一些重构警告(但不是全部) ,它可以衡量代码复杂度。

目前为止,除了pylint 之外,我还使用了所有这些代码检查工具,具体方法请参见我的文章《如何检查我的 Python 代码》。

但如果我完全停止使用pylint呢?

我只会失去一些重构提示信息,但我估计大部分都会在代码审查过程中被发现。作为交换,我可以摆脱所有这些无意义的# pylint: disable注释。(大约 5000 行代码,却有 34 条这样的注释)

这就是我停止使用 pylint 并将其从我的 CI 脚本中移除的原因。在此向 pylint 的作者和维护者们致歉:你们这些年来做得非常出色,但我现在认为是时候使用更新更好的工具了。

接下来会发生什么?

这并非我寻找能帮助我编写更好 Python 代码的工具的漫长征程的终点。你可以在Hello falke8中阅读故事的其余部分。

感谢您读到这里 :)

我很想听听大家的意见,所以请随时在下方留言,或者阅读反馈页面了解更多与我联系的方式。


  1. 顺便一提,在《试用 mypy》一文的结尾,我提到我很好奇 mypy 在大规模重构过程中能否有所帮助。结果证明,它确实帮了我大忙,甚至比我预想的还要好! 

文章来源:https://dev.to/dmerejkowsky/bye-bye-pylint-4chh