对于大多数人来说,学习和实践新知识可能没有统一的定式,但在使用的工具上我们可以做到统一,统一的工具能让我们写出更规范、质量更好的代码。
因此无论对于刚入门的小白还是正在成长的新人来说,学会使用辅助工具是十分有必要的。本文将会介绍一些笔者在日常个人项目和工作中都会用到,并最后向团队推广使用的Python辅助工具。
不过代码当中有一个部分,却是能被自动化并且通过工具来检查,最后帮助我们完成的修改的,即代码格式化(CodesFormatting)。
代码格式化其实旨在改善代码的可读性从而提高整体的代码质量,即可以理解成为我们在上学时,老师常常会提到的但却经常容易被人忽视的考试得分项:保持「卷面整洁」。
读者可以分别对比以下两段代码:
#withoutcodeformattingdefthis_is_a_function_without_formatting(var_a,var_b,var_c,var_d,with_long_arguments):ifvar_a!=0andvar_b==1andnot(var_corvar_d)andlen(with_long_arguments)<10:do_something()foo=this_is_a_function_without_formatting(var_a=1,var_b=2,var_c=3,var_d=4,with_long_arguments=with_long_arguments=[5,6,7,8,9])#codeformattingdefthis_is_a_function_with_formatting(var_a,var_b,var_c,var_d,with_long_arguments,):if(var_a!=0andvar_b==1andnot(var_corvar_d)andlen(with_long_arguments)<10):do_something()foo=this_is_a_function_with_formatting(var_a=1,var_b=2,var_c=3,var_d=4,with_long_arguments=[5,6,7,8,9],)相比未格式化的代码,可以看到格式化后的代码更加规范、可读性更好。
所以使用Black之后上述代码将会是这样:
使用Black的方式很简单,通过pipinstallblack安装到你的Python环境中,然后在代码中运行以下命令即可:
black/path/to/your/python/file.py就是这么简单!
当然Black也提供了一些可供配置的选项,但工具本身的默认设置已经非常完善,不需要我们再额外调整了,需要调整的配置项也是寥寥几个,我们可以通过命令行来查看并使用对应的配置项:
#pyproject.toml[tool.black]line-length=89skip-string-normalization=true之后在包含该配置文件的目录下,只需要执行Black命令以及待格式化的代码文件路径即可,而无须指定相应的命令行选项参数。
Python社区的生态一直都是十分丰富,所以在开发项目的过程中,我们往往会使用到多个库或同一个库中的多个模块。但在缺乏规范的情况下,被导入的部分可能会凌乱、无组织地分散在我们的代码中,所以当我们复现代码时,很可能就会因为忘记导入某些模块而报错。
就像官方给出的示例代码一样:
针对以上存在的一些导入问题,要么我们就干脆每个导入的部分只占一行,要么就是来自同一模块下的导入部分都连接在一起。
所以这时候isort就排上了用场,上面的代码就会被格式化成以下形式:
这样我们的代码不仅看起来清爽了许多,同时也能准确定位到我们已经导入的部分有哪些。
使用isort之前同样我们也需要通过pip命令进行安装:
pipinstallisort然后用法和Black一样,在使用时指定需要格式化的Python代码文件路径即可:
isort/path/to/your/python/file.pyisort不会像Black那样「执拗」且充满不妥协精神,它存在大量可供选择的配置选项和定制化接口,以满足不同风格、团队规范的需要,官方文档上有着详细的记录:
isort同样可以将配置项写入配置文件中,并且也可以和Black搭配使用,不过需要指定相应的兼容配置项profile并将其设置为black,这里笔者仅以pyproject.toml文件为例:
以上配置项仅仅只是笔者在平时个人或工作项目中的配置示例,受限于篇幅读者可以根据自己的需要查阅isort的官方文档以了解更多的可配置项内容。
由于像Python、JavaScript这些动态类型的解释型语言,不具备Java、Go这样静态类型的编译型语言在编译时的检查机制,因此我们无法事先感知到代码当中存在的一些问题,只有运行代码时这些问题才会被暴露。比如像如下的代码:
在IDE中此时这段代码并并没有出现任何问题,我们仅知道multiply()只有一个numbers参数,但到底这个参数可以传递哪些类型的数据我们却不得而知。于是当我们运行上述代码时,问题就出现了:
可以看到,multiply()函数应该是只支持包含数值型的容器类型数据,因此当我们向其传入一个包含字符串型的集合时必然会出现错误。
这也就是动态类型的解释型语言存在的一个弊端,所以在开发中不少程序员都会体会到「动态一时爽,重构火葬场」的心路历程。
因此从结果可以看到,当我对代码简单修改并添加类型注解之后,IDE很快速地就能发现了与代码要求不相符的部分,并对其给出了详细提示。
目前关于Python的静态检查工具有许多,总的来说可以分为两类:代码规范检查和代码逻辑检查。
在前面关于Black的内容中笔者有曾提到过PEP8规范,其中有部分的规范内容是可以通过像Black和isort这样的工具来实现自动格式化,而有些部分可能就需要借助代码规范检查工具才能发现并手动处理,比如一行代码是否超过80个字符、是否存在已导入未使用的库或模块等,这些都是需要代码规范检查工具介入的地方。
目前大部分IDE也具备了类似的代码规范检查功能,我们可以很轻易发现大部分不合乎代码规范的问题:
除此之外,Python社区也有类似的工具供我们选择,大部分我们都可以在PyQCA的项目仓库中找到:
这里笔者就主要选择flake8,因为它是多个工具的集合,在使用上也相对方便。在使用前先通过pip命令工具进行安装:
pipinstallflake8之后我们可以直接像使用Black或isort那样在执行命令后紧接着对应的Python代码文件路径。比如前面的IDE代码:
$flake8--helpusage:flake8[options]filefile...positionalarguments:filenameoptionalarguments:-h,--helpshowthishelpmessageandexit-v,--verbosePrintmoreinformationaboutwhatishappeninginflake8.Thisoptionisrepeatableandwillincreaseverbosityeachtimeitisrepeated.--output-fileOUTPUT_FILERedirectreporttoafile.--append-configAPPEND_CONFIGProvideextraconfigfilestoparseinadditiontothefilesfoundbyFlake8bydefault.Thesefilesarethelastonesreadandsotheytakethehighestprecedencewhenmultiplefilesprovidethesameoption.#各位可以在终端自行尝试,查看完整的参数列表和解释不过相比于IDE来说,我们还可以为flake8设置相应的配置文件,通过该文件可以配置选择忽略掉哪些检查项、排除哪些文件等。
我们只需要在包含代码文件的根文件路径中包含一个名为.flake8特殊命名的文件,然后在当中试着以如下方式填写相应的内容:
[flake8]ignore=#F401importedbutunusedF401,#E225missingwhitespacearoundoperatorE225,#E501linetoolongE501,#W292nonewlineatendoffileW292exclude=.git,__pycache__,dist这等价于命令行:
flake8--ignoreF401,E225,E501,W292\--exclude.git,__pycache__,dist\main.py最后输出的结果将会比默认配置下输出的信息输出更为简洁:
$flake8main.pymain.py:10:11:E226missingwhitespacearoundarithmeticoperatormain.py:11:10:W503linebreakbeforebinaryoperatormain.py:11:10:E128continuationlineunder-indentedforvisualindent然后我们只需要根据信息上所示找到对应的代码行数进行修改即可。
由于PEP484提案的出现,开始使得Python也可以像静态类型语言那样为变量、参数或函数返回值标注相应的类型,以便能配合IDE和相应的工具,共同发现代码当中可能存在的逻辑错误或是类型错误等问题,从而改善代码质量并提高程序健壮性。
总而来说,目前Python社区主要有这么以下几个流行的静态类型检查工具(Staictypechecker),根据项目创建日期由远及近为:
这里笔者只以mypy为例,如果读者对于其他的静态类型检查工具有特别偏好,可以自行挑选并阅读官方文档后使用。使用前需要通过pip命令进行安装:
pipinstallmypy简单来说,如果我们希望静态类型检查工具发挥作用,那么就最好编写带有类型注解的Python代码。
这里我们仍然以前面没有添加类型注解的multiply()函数为例:
$mypymain.pySuccess:noissuesfoundin1sourcefile只有当我们为其添加上类型注解,才会发挥mypy应有的作用:
因此针对上述问题我们可以手动预先修改,对于total变量,我们即可以为其添加和返回值一样的类型注解:
defmultiply(numbers:Sequence[Numeric])->Numeric:total:Numeric=1fornumberinnumbers:total*=numberreturntotal当然mypy也提供了一种让我们忽略掉可能被检查到并提示错误的机制,即我们可以通过注释来进行标注:
defmultiply(numbers:Sequence[Numeric])->Numeric:total=1fornumberinnumbers:total*=number#type:ignorereturntotal无论是使用以上哪种方式,最后我们都不会再得到Incompatibletypesinassignment的错误提示了:
默认情况下mypy的检查是相对严格的,也会尽可能地发现Python代码中存在问题的部分;当然这些检查项都会有与之对应的配置项,因此我们如果并不需要那么多提示的检查或提示信息,那么也可以通过配置文件来进行预先设定:
到本小节为止,本章所提及的工具多以命令行的形式使用。但相信有不少和我一样的「懒人」,希望在尽可能不敲命令行的情况下,也能使用到这些工具以帮助我们改善或提高Python代码的质量。
因此最好的方式就是我们能将其整合到一块,然后只需要在每次完成代码时在统一运行、调用即可。
大部分工具或多或少都会存在可供配置的选项,因此整合这些工具配置项的最好方式就是将其全都放在一个配置文件中,比如pyproject.toml:
像Black和isort这样能够实时将代码格式化的工具,我们可以将其整合到开发或编写代码所用的IDE工具中,这样就可以在每次保存代码时自动进行格式化,而不需要我们手动运行。
在VSCode中当我们安装了微软官方的Python插件时,默认就可以在用户设置选项中搜索到关于格式化的有关设置,然后我们只需要:
之后我们再另外勾选VSCode中editor.formatOnSave选项让代码在保存时自动格式化:
注:本小节演示的是PycharmCommunity社区版,而PycharmProfessional专业付费版已经内置了FileWatchers,可以无需安装。
安装完成后我们就需要接着在Pycharm的设置页中找到FileWatchers的设置项,并在当中完成设置:
设置完成之后,我们每次只需要保存代码时就会自动格式化。
除了与IDE整合这样的方式之外,我们还可以将所有工具的执行命令放在一个脚本文件中,每次只需要调用该脚本文件即可执行所有命令。
比如说Shell脚本:
WORKDIR=$PWDisort-v$WORKDIR\&&black--skip-string-normalization-v$WORKDIR\&&flake8$WORKDIR\&&mypy--strict$WORKDIR又比如使用Makefile文件并搭配make构建命令:
本文主要介绍了一些能够提升Python代码质量的工具,比如代码格式化工具、静态检查工具等。
当然不仅是Python,目前主流的编程语言都会有许多配套的工具链或类似的设施。
通过对这些工具的使用一方面可以提高我们代码的可读性与质量,另一方面能够及早发现当中可能存在的某些代码问题从而整体提高程序的健壮性。