上周帮同事排查一个线上问题,代码打开他的检查项目目录,满屏的代码红色波浪线。我问他:“你平时不跑lint吗?检查”他愣了一下:“能跑就行,管那么多干嘛。代码”结果就是检查,一个变量名拼错,代码在测试环境没触发,检查上了生产直接报错回滚。代码这事让我觉得,检查有必要把Python代码检查这件事说清楚。代码
代码检查不是检查找茬,也不是代码为了满足某种“代码洁癖”。它最直接的检查价值是帮你提前发现那些肉眼容易忽略的错误。比如未使用的代码变量、未定义的名称、括号不匹配、缩进混乱。Python的缩进是语法的一部分,一个tab和空格的混用,解释器可能不报错,但逻辑已经偏了。这种问题靠人眼一行行看,累且不可靠。
静态检查工具不运行代码,只分析源码结构。它把代码解析成抽象语法树,然后按规则遍历。常见的检查项包括:语法错误、导入未使用、变量赋值后从未读取、函数参数默认值可变、裸except、重复的字典键。这些在运行时未必立刻暴露,但都是隐患。
举个例子,下面这段代码看起来没问题:
def add_item(item, items=[]): items.append(item) return items 调用两次add_item(1)和add_item(2),结果会是[1, 2]。因为默认参数items在函数定义时只创建一次。Pylint和Flake8都能捕获这个陷阱。人写代码时容易忽略,工具不会。
我自己的组合是Flake8 + Pylint + Mypy。Flake8快,管风格和基础错误,配置少,适合放进pre-commit。Pylint规则更细,会检查代码重复、设计复杂度,但误报也多,需要调.pylintrc。Mypy管类型,如果你用了类型注解,它能发现参数类型不匹配、返回值类型错误。
有人问,Black和isort算不算检查?严格说它们是格式化工具,不是检查器。但格式化能消除大量风格争议,让检查器专注逻辑问题。我的习惯是保存时自动Black,提交前跑Flake8和Mypy。
光装工具没用,得让它自动跑。我在项目根目录放.pre-commit-config.yaml,配置好钩子。每次git commit,pre-commit会先跑Flake8和Black,不通过就拒绝提交。这样代码进仓库前已经过一轮过滤。
CI环节再加一道。GitHub Actions里加一个job,跑flake8 . --max-line-length=100和mypy src。有人觉得CI跑检查浪费时间,但比起上线后回滚,这点时间不算什么。我见过一个团队,CI检查跑了三个月后,线上因低级错误导致的故障下降了七成。数据不一定精确,趋势是真实的。
工具是辅助,不是圣经。Pylint说too-many-instance-attributes,但你的类就是需要那么多属性,加个# pylint: disable=too-many-instance-attributes就行。关键是要理解每条告警背后的原因,而不是无脑忽略或无脑改。我见过有人为了过检查,把函数拆得七零八落,可读性反而下降。这就本末倒置了。
代码检查的最终目标是让代码更可靠、更易维护。它不能替代测试,也不能替代code review。但它能在你按下回车之前,拦住那些“手滑”级别的错误。花半小时配好工具,后面省下的调试时间远不止半小时。