再见,pylint
介绍
痛点
翻页
类别
短切工的兴起
接下来会发生什么?
wemake-python-styleguide
原文发表于我的博客。
我使用pylint已经将近十年了。
十年后,我决定不再使用它了。
原因如下。
介绍
我们先来看一个例子。请看下面这段显然是错误的代码:
def foo():
...
if __name__ == " __main__"
foo(1, 2, 3)
以下是运行 pylint 后可能的输出结果:
$ pylint foo.py
foo.py:4: [E1121(too-many-function-args),]
Too many positional arguments for function call
现在让我们来看看我在使用 pylint 时遇到的一些问题。
痛点
初始设置
pylint 的初始配置总是有点麻烦。不过,只要遵循一些建议,就能顺利完成。
假阴性
pylint 的一个常见问题是误报率过高。也就是说,pylint 会认为某些代码有问题,但实际上代码完全没问题。
例如,每当我的类主要包含数据时,我都喜欢使用attrs库,就像这样:
import attr
@attr.s
class Foo:
bar = attr.ib()
baz = attr.ib()
这几行代码就给了我一个易于阅读的程序__repr__、一套完整的比较方法、合理的构造函数(以及其他一些功能),而且没有任何样板代码。
但是当我运行pylint这个文件时,却得到:
foo.py:3: [R0903(too-few-public-methods), Foo] Too few public methods (0/2)
当然,要求每个类至少有两个公共方法是完全合理的。大多数情况下,如果一个类只有一个公共方法,最好直接使用函数,像这样:
# 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)
但是,pylint 并不知道所有“动态”添加的优秀方法,因此attr错误地认为我们的设计是错误的。
因此,如果在持续集成 (CI) 过程中运行 pylint,并且发现任何错误导致构建失败,则必须插入特殊格式的注释以在本地禁用此警告:
import attr
# pylint: disable=too-few-public-methods
@attr.s
class Foo:
...
这样做很快就会让人厌烦,尤其是在每次升级 pylint 时都会添加一大堆新的检查项。有时它们会发现代码中的新问题,但你仍然需要逐个检查每个新错误,以确定它是误报还是真正的问题。
但到目前为止,我已经成功克服了这些痛点。那么,是什么改变了这一切呢?
翻页
发生了两件事:
我发现 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 为什么没有按预期运行时,Fatal和Info类别才有用。
短切工的兴起
我意识到,对于几乎所有 pylint 类别,我都可以使用其他代码检查工具(不仅仅是 mypy)。
目前为止,除了pylint 之外,我还使用了所有这些代码检查工具,具体方法请参见我的文章《如何检查我的 Python 代码》。
但如果我完全停止使用pylint呢?
我只会失去一些重构提示信息,但我估计大部分都会在代码审查过程中被发现。作为交换,我可以摆脱所有这些无意义的# pylint: disable注释。(大约 5000 行代码,却有 34 条这样的注释)
这就是我停止使用 pylint 并将其从我的 CI 脚本中移除的原因。在此向 pylint 的作者和维护者们致歉:你们这些年来做得非常出色,但我现在认为是时候使用更新更好的工具了。
接下来会发生什么?
这并非我寻找能帮助我编写更好 Python 代码的工具的漫长征程的终点。你可以在Hello falke8中阅读故事的其余部分。
感谢您读到这里 :)
我很想听听大家的意见,所以请随时在下方留言,或者阅读反馈页面了解更多与我联系的方式。
-
顺便一提,在《试用 mypy》一文的结尾,我提到我很好奇 mypy 在大规模重构过程中能否有所帮助。结果证明,它确实帮了我大忙,甚至比我预想的还要好! ↩