如何使用 Pylint 来规范 Python 代码风格(来自IBM)
Pylint是什么
Pylint是一个Python代码分析工具,它分析Python代码中的错误,查找不符合代码风格标准(Pylint默认使用的代码风格是PEP8,具体信息,请参阅参考资料)和有潜在问题的代码。目前Pylint的最新版本是pylint-0.18.1。
- Pylint是一个Python工具,除了平常代码分析工具的作用之外,它提供了更多的功能:如检查一行代码的长度,变量名是否符合命名标准,一个声明过的接口是否被真正实现等等。
- Pylint的一个很大的好处是它的高可配置性,高可定制性,并且可以很容易写小插件来添加功能。
- 如果运行两次Pylint,它会同时显示出当前和上次的运行结果,从而可以看出代码质量是否得到了改进。
- 目前在eclipse的pydev插件中也集成了Pylint。
Pylint具体介绍
Pylint的安装
Pylint可以用于所有高于或者等于2.2的Python版本兼容。需要logilab-astng(version>=0.14)和logilab-common(version>=0.13)的包(具体信息,请参阅参考资料),如果是Python版本低于2.3,那么它还需要optik包(本文接下来的示例暂不考虑这种情况)。
Pylint所用到的所有的包的下载地址
logilab-astng的最新包下载:http://www.logilab.org/856/
logilab-common的最新包下载:http://www.logilab.org/848/
optik的包下载:http://optik.sourceforge.net/
Pylint的最新包下载:http://www.logilab.org/project/pylint
Pylint在Linux上的安装
1.在Linux上,首先安装Python的包(高于版本2.2),并在环境变量$PATH中添加Python可执行文件的路径。
2.下载Pylint、logilab-astng(version>=0.14)和logilab-common(version>=0.13)的包,使用tarzxvf*.tar.gz解压缩这些包。
3.依次进入logilab-astng、logilab-common和Pylint解开的文件夹中,运行命令Pythonsetup.pyinstall来安装。
4.安装完成后,就可以通过pylint[options]module_or_package来调用Pylint了。
Pylint在Windows上的安装
1.安装Python的包(高于版本2.2),右键单击桌面上的我的电脑图标,选择属性,高级,环境变量,在$PATH中添加Python的安装路径,如C:\Python26\。
2.使用解压缩工具解压缩所有的包。
3.打开命令行窗口,使用cd依次进入logilab-astng、logilab-common和Pylint解开的文件夹中,运行命令pythonsetup.pyinstall来安装。
4.安装完成后,在Python的安装路径下出现一个Scripts文件夹,里面包含一些bat脚本,如pylint.bat等。
5.为了使调用pylint.bat的时候不需要输入完整路径,在Python的安装目录下创建pylint.bat的重定向文件,这是一个纯文本文件pylint.bat,里面包含pylint.bat的实际路径,如:C:\Python26\Scripts\pylint.bat。
6.安装完成后,可以通过pylint[options]module_or_package来调用Pylint了。
Pylint的调用
清单1.Pylint的调用命令
pylint[options]module_or_package
使用Pylint对一个模块module.py进行代码检查:
- 1.进入这个模块所在的文件夹,运行pylint[options]module.py
这种调用方式是一直可以工作的,因为当前的工作目录会被自动加入Python的路径中。 - 2.不进入模块所在的文件夹,运行pylint[options]directory/module.py
这种调用方式当如下条件满足的时候是可以工作的:directory是个Python包(比如包含一个__init__.py文件),或者directory被加入了Python的路径中。
使用Pylint对一个包pakage进行代码检查:
- 1.进入这个包所在文件夹,运行pylint[options]pakage。
这种调用方式是一直可以工作的,因为当前的工作目录会被自动加入Python的路径中。 - 2.不进入包所在的文件夹,运行pylint[options]directory/pakage。
这种情况下当如下条件满足的时候是可以工作的:directory被加入了Python的路径中。比如在Linux上,exportPYTHONPATH=$PYTHONPATH:directory。
此外,对于安装了tkinter包的机器,可以使用命令pylint-gui打开一个简单的GUI界面,在这里输入模块或者包的名字(规则同命令行),点击Run,Pylint的输出会在GUI中显示。
Pylint的常用命令行参数
- -h,--help
显示所有帮助信息。
- --generate-rcfile
可以使用pylint--generate-rcfile来生成一个配置文件示例。可以使用重定向把这个配置文件保存下来用做以后使用。也可以在前面加上其它选项,使这些选项的值被包含在这个产生的配置文件里。如:pylint--persistent=n--generate-rcfile>pylint.conf,查看pylint.conf,可以看到persistent=no,而不再是其默认值yes。
- --rcfile=
指定一个配置文件。把使用的配置放在配置文件中,这样不仅规范了自己代码,也可以方便地和别人共享这些规范。
- -i
,--include-ids= 在输出中包含message的id,然后通过pylint--help-msg=
来查看这个错误的详细信息,这样可以具体地定位错误。 - -r
,--reports= 默认是y,表示Pylint的输出中除了包含源代码分析部分,也包含报告部分。
- --files-output=
将每个module/package的message输出到一个以pylint_module/package.[txt|html]命名的文件中,如果有report的话,输出到名为pylint_global.[txt|html]的文件中。默认是输出到屏幕上不输出到文件里。
- -f
,--output-format= 设置输出格式。可以选择的格式有text,parseable,colorized,msvs(visualstudio)和html,默认的输出格式是text。
- --disable-msg=
禁止指定id的message.比如说输出中包含了W0402这个warning的message,如果不希望它在输出中出现,可以使用--disable-msg=W0402
Pylint的输出
Pylint的默认输出格式是原始文本(rawtext)格式,可以通过-f
源代码分析部分:
对于每一个Python模块,Pylint的结果中首先显示一些"*"字符,后面紧跟模块的名字,然后是一系列的message,message的格式如下:
MESSAGE_TYPE:LINE_NUM:[OBJECT:]MESSAGE
MESSAGE_TYPE有如下几种:
(C)惯例。违反了编码风格标准
(R)重构。写得非常糟糕的代码。
(W)警告。某些Python特定的问题。
(E)错误。很可能是代码中的错误。
(F)致命错误。阻止Pylint进一步运行的错误。
清单2.Pylint中的utils模块的输出结果
*************Moduleutils
C:88:Message:Missingdocstring
R:88:Message:Toofewpublicmethods(0/2)
C:183:MessagesHandlerMixIn._cat_ids:Missingdocstring
R:183:MessagesHandlerMixIn._cat_ids:Methodcouldbeafunction
R:282:MessagesHandlerMixIn.list_messages:Toomanybranches(14/12)
报告部分:
在源代码分析结束后面,会有一系列的报告,每个报告关注于项目的某些方面,如每种类别的message的数目,模块的依赖关系等等。具体来说,报告中会包含如下的方面:
- 检查的module的个数。
- 对于每个module,错误和警告在其中所占的百分比。比如有两个moduleA和B,如果一共检查出来4个错误,1个错误是在A中,3个错误是在B中,那么A的错误的百分比是25%,B的错误的百分比是75%。
- 错误,警告的总数量。
使用Pylint分析Python代码的具体示例
下面是一个从xml文件中读取一些值并显示出来的一段Python代码dw.py,代码如下:
清单3.源码
importstring #!/usr/bin/envpython importxml.dom.minidom xmlDom=xml.dom.minidom.parse("identity.xml") organizations=xmlDom.getElementsByTagName('DW') fororginorganizations: products=org.getElementsByTagName('linux') forproductinproducts: print'ID:'+product.getAttribute('id') print'Name:'+product.getAttribute('name') print'WordCount:'+product.getAttribute('count')
清单4.identity.xml的内容
这时候使用Pylint的结果(这是从html格式的输出中拷贝的)为:
清单5.Pylint的分析结果
*************Moduledw
C:1:Missingdocstring
C:5:OperatornotprecededbyaspacexmlDom=xml.dom.minidom.parse("identity.xml")^
C:5:Invalidname"xmlDom"(shouldmatch(([A-Z_][A-Z0-9_]*)|(__.*__))$)
C:6:Invalidname"organizations"(shouldmatch(([A-Z_][A-Z0-9_]*)|(__.*__))$)
Report部分省略
输出中第一部分是源代码分析,第二部分是报告。输出结果中有这么多信息,从哪里开始分析呢?首先使用如下的步骤来分析代码:
1.因为输出结果太长,所以可以先不让它输出报告部分,先根据源代码分析部分来找出代码中的问题。使用选项"--reports=n"。
2.使用选项"--include-ids=y"。可以获取到源代码分析部分每条信息的ID。
清单6.使用pylint--reports=n--include-ids=ydw.py的结果
*************Moduledw
C0111:1:Missingdocstring
C0322:5:OperatornotprecededbyaspacexmlDom=xml.dom.minidom.parse("identity.xml")^
C0103:5:Invalidname"xmlDom"(shouldmatch(([A-Z_][A-Z0-9_]*)|(__.*__))$)
C0103:6:Invalidname"organizations"(shouldmatch(([A-Z_][A-Z0-9_]*)|(__.*__))$)
每个信息前面都会加上一个id,如果不理解这个信息的意思,可以通过pylint--help-msg=id来查看。
清单7.使用pylint--help-msg=C0111的结果
C0111:*Missingdocstring*
Usedwhenamodule,function,classormethodhasnodocstring.Somespecial
methodslike__init__doesn'tnecessaryrequireadocstring.
Thismessagebelongstothebasicchecker.
3.开始分析每个源代码中的问题。从上面知道,第一个问题的原因是缺少docstring,在代码中增加docstring,修改后的代码如下:
清单8.增加docstring修改后的源码
#!/usr/bin/envpython """Thisscriptparsethecontentofaxmlfile""" importxml.dom.minidom xmlDom=xml.dom.minidom.parse("identity.xml") organizations=xmlDom.getElementsByTagName('DW') fororginorganizations: products=org.getElementsByTagName('linux') forproductinproducts: print'ID:'+product.getAttribute('id') print'Name:'+product.getAttribute('name') print'WordCount:'+product.getAttribute('count')
重新运行pylint--reports=n--include-ids=ydw.py,结果为:
清单9.运行结果
*************Moduledw
C0322:7:Operatornotprecededbyaspace
xmlDom=xml.dom.minidom.parse("identity.xml")
^
C0103:7:Invalidname"xmlDom"(shouldmatch(([A-Z_][A-Z0-9_]*)|(__.*__))$)
C0103:8:Invalidname"organizations"(shouldmatch(([A-Z_][A-Z0-9_]*)|(__.*__))$)
可以看到源代码中的第一个问题已被解决。
4.关于第二个C0322的问题,这里的分析结果说明得比较清楚,是代码第七行中的等号运算符两边没有空格。我们在这里加上空格,重新运行pylint--reports=n--include-ids=ydw.py,结果为:
清单10.运行结果
*************Moduledw
C0103:7:Invalidname"xmlDom"(shouldmatch(([A-Z_][A-Z0-9_]*)|(__.*__))$)
C0103:8:Invalidname"organizations"(shouldmatch(([A-Z_][A-Z0-9_]*)|(__.*__))$)
5.可以看到现在问题只剩下C0103了。这里的意思是变量命名规则应该符合后面正则表达式的规定。Pylint定义了一系列针对变量,函数,类等的名字的命名规则。实际中我们不一定要使用这样的命名规则,我们可以定义使用正则表达式定义自己的命名规则,比如使用选项--const-rgx='[a-z_][a-z0-9_]{2,30}$',我们将变量xmlDom改为xmldom,代码如下:
清单11.将变量xmlDom改为xmldom后的源码
#!/usr/bin/envpython """Thisscriptparsethecontentofaxmlfile""" importxml.dom.minidom xmldom=xml.dom.minidom.parse("identity.xml") organizations=xmldom.getElementsByTagName('DW') fororginorganizations: products=org.getElementsByTagName('linux') forproductinproducts: print'ID:'+product.getAttribute('id') print'Name:'+product.getAttribute('name') print'WordCount:'+product.getAttribute('count')
运行pylint--reports=n--include-ids=y--const-rgx='[a-z_][a-z0-9_]{2,30}$'dw.py,结果中就没有任何问题了。
6.如果希望一个组里的人都使用这些统一的规则,来规范一个部门的代码风格。比如说大家都使用--const-rgx='[a-z_][a-z0-9_]{2,30}$'作为命名规则,那么一个比较便捷的方法是使用配置文件。
使用pylint--generate-rcfile>pylint.conf来生成一个示例配置文件,然后编辑其中的--const-rgx选项。或者也可以直接pylint--const-rgx='[a-z_][a-z0-9_]{2,30}$'--generate-rcfile>pylint.conf,这样生成的配置文件中--const-rgx选项直接就是'[a-z_][a-z0-9_]{2,30}$'了。
以后运行Pylint的时候指定配置文件:pylint--rcfile=pylint.confdw.py
这样Pylint就会按照配置文件pylint.conf中的选项来指定参数。在一个部门中,大家可以共同使用同一个配置文件,这样就可以保持一致的代码风格。
7.如果把report部分加上,即不使用--reports=n,可以看到报告部分的内容。
结束语
本文通过详细的理论介绍和简单易懂的实例全面介绍了Python代码分析工具Pylint。相信读者看完后一定可以轻松地将Pylint运用到自己的开发工程中。
相关主题
- Pylint官方网站。
- logilab-astng的最新包下载。
- logilab-common的最新包下载。
- optik的包下载。
- Pylint的最新包下载。
- 查看Python代码风格标准PEP8--StyleGuideforPythonCode下载。
- 更多关于Python内容,请参考developerWorks上Python专题。