必须将自定义 checker 注册到 pylint 的 register 函数并确保模块可 import;通过 –load-plugins=模块名加载;ide 需单独配置参数;注意 ast 变更与性能优化。

怎么让 pylint 加载你写的自定义 checker
必须把 checker 类注册进 pylint 的 register 函数里,且文件得放在 python 路径中可 import 的位置。否则 pylint 启动时根本不会扫描它。
- 推荐做法:把 checker 写成一个独立模块(比如
my_checker.py),确保能被import my_checker;然后在模块末尾加def register(linter): linter.register_checker(MyChecker(linter)) - 运行时加载:用
pylint --load-plugins=my_checker,注意这里填的是模块名,不是文件路径 - 常见错误是把
.py文件直接扔进项目根目录就以为能自动识别——不行,pylint 不会递归扫描当前目录找 checker - 如果报错
Unable to import 'my_checker',先试python -c "import my_checker"确认导入通路是否正常
checker 类里怎么捕获变量未定义的访问
不是所有“未定义变量”都走同一个钩子。astroid 解析后,未声明就使用的变量通常触发 visit_name,但前提是该变量不在作用域链中被找到。
- 在自定义 checker 的
visit_name方法里,调用node.frame().locals.get(node.name)查本地符号表;若返回空,再结合node.scope()排查是否属于闭包/全局遗漏 - 别只依赖
node.inferred()——对未定义变量它会抛InferenceError,需 try-catch,否则整个检查会中断 - 注意
visit_name也会匹配函数名、类名等,要加if node.name.isidentifier() and not hasattr(node, 'assign_to'):过滤掉赋值左侧 - 误报高发场景:动态属性(
getattr(obj, 'xxx'))、__getattr__、或 pytest 中的 fixture 名——这些需要白名单绕过
为什么你的 checker 在命令行生效但在 IDE(如 VS Code)里不触发
因为 IDE 通常用自己缓存的 pylint 可执行路径和配置,不一定读取你当前终端环境的 --load-plugins 参数。
- VS Code 中要改
python.linting.pylintArgs设置项,在数组里显式加"--load-plugins=my_checker" - pycharm 需在 Settings → Tools → Python Linting → Pylint → Additional Arguments 填入相同参数
- 更隐蔽的问题:IDE 启动 pylint 时可能用了 virtualenv 外的全局 Python,导致
import my_checker失败——检查 IDE 底部状态栏 Python 解释器路径是否和命令行一致 - 临时验证方法:在 checker 的
register函数开头加print("my_checker loaded"),看 IDE 的 lint 输出里有没有这行
如何避免自定义 checker 拖慢整体 lint 速度
pylint 默认会对每个 AST 节点调用所有 checker 的对应 visit 方法,哪怕你只关心 Call 节点,visit_name 仍会被大量无关节点触发。
立即学习“Python免费学习笔记(深入)”;
- 在 checker 初始化时,用
self.linter.register_transform或直接重写enable/disable控制活跃范围,比在每个 visit 里 if-return 更高效 - 对耗时操作(比如正则匹配、外部 API 调用)务必加缓存,例如用
functools.lru_cache包装校验函数 - 避免在
visit_module里做全文件字符串扫描——改用visit_call+node.func.as_string()定向抓目标函数调用 - 上线前用
pylint --profile your_file.py看各 checker 的耗时占比,visit_const和visit_name是最容易成为性能瓶颈的两个钩子
最常被忽略的一点:pylint 版本升级后 AST 节点结构可能微调,比如 node.parent 在 2.15+ 改为 node.parent_node。不盯紧 release note,你的 checker 可能悄无声息地跳过所有检查。