page contents

Python 工具链让你写的代码更规范

随着 PEP8 规范的提出,Python 社区也有了自己的一套关于 Python 代码的规范指导。但...

从以前一个不会敲 Python 代码的小白,到现在敲 Python 代码贼六的老鸟的过程中,除了通过大量的学习、实践让自己现在更容易写出 Pythonic 的代码外,大部分时间里还是多亏着有着许多好用的工具辅助我,去检查我代码中的问题,帮助我写出更规范的代码。

学习和实践新知识可能对于大多数人来说都没有统一的定式,但使用工具至少可以让我们大家风格统一也更容易和他人协作共事。因此无论对于刚入门的小白还是正在成长的新人来说,会使用能辅助你写出更规范的代码的工具也是十分有必要的。

black

随着 PEP8 规范的提出,Python 社区也有了自己的一套关于 Python 代码的规范指导。但我相信,大多数人其实并不会条分缕析地去浏览或记下规范里的大多数内容,因为这些内容往往都过于琐碎,以至于在实际开发中我们很难做到去一边写代码一边去检查我们是否合乎 PEP8 规范。所以最好的方法就是借助格式化工具帮我们完成这一机械的操作。

早期 Python 著名的格式化工具的有 autopep8 和 Google 的 yapf,但在实际过程中或多或少需要一些配置。

如果你使用过 Go 语言,那可能会对 Go 自带的 gofmt 赞叹不已。因为它既不会要求你进行多余的配置,还能在每次保存代码时强制帮你进行格式化,所以无论和你协作的同事们代码风格和个人习惯如何,它都能将其转换成符合工程的、规范的样式。

而 black 就是类似于 gofmt 这样号称 「不妥协」(uncompromising)的 Python 格式化工具,它让我们将更多的时间和精力放在编写代码上,而无需过度关注于格式化的配置细节。因此当我们在团队协作中只要是使用了 black 工具之后,大部分情况下代码看起来都是十分统一的。

比如说下面这一段代码(来源于官方示例):



def very_important_function(template: str, *variables, file: os.PathLike, engine: str, header: bool = True, debug: bool = False):
    """Applies `variables` to the `template` and writes to `file`."""
    with open(file, 'w'as f:
        ...

由于 very_important_function 已经加上了类型注解,因此在现有的部分参数所占宽度的基础上又扩展了一些,所以如果我们显示器的宽度不够时,就需要横向拖动才能查看参数信息;而最好的办法就是采取竖向的方式进行排列,便于我们能自上而下的一览无遗。所以使用 black 之后上述代码将会是这样:



def very_important_function(
    template: str,
    *variables,
    file: os.PathLike,
    engine: str,
    header: bool = True,
    debug: bool = False,
)
:

    """Applies `variables` to the `template` and writes to `file`."""
    with open(file, "w"as f:
        ...

可以看出,经过格式化后的函数其参数层次分明地对齐,可读性大大的增强了;并且如果需要对函数中的参数进行注释或增加,直接新增或减少一行即可,丝毫不会额外调整其他参数的位置。

尽管说 black 还是一个实验项目,但它早已频繁被使用在一些主流的 Python 开源项目中,比如 Pandas、SQLAlchemy、Pytest 等,所以就算在实际开发中我们一样可以没有顾虑地使用它。

使用 black 的方式很简单,通过 pip install black 安装到你的 Python 环境中,然后在代码中运行:



black <项目文件夹 / 源码文件夹 / .py 文件等>

black 命令行也提供了一些参数项(但不会太多),我们大部分时候即使我们使用默认的也是足够了的,具体我们可以参考 black 的官方文档。

isort

isort 是一个名为 PyCQA(Python Code Quality Authority)组织所维护的代码质量工具中的其中一个开源项目,它同样是用来对代码进行格式化。但不同于 black 的是,它主要用来格式化我们 import 的库或模块。

Python 社区的生态一直都是十分丰富,所以在开发项目的过程中我们往往会使用到多个库或同一个库中的多个模块,但在缺乏规范的情况下,被导入的部分可能会凌乱、无组织地分散在我们的代码中,所以当我们复现代码时很可能就会因为忘记导入某些模块而报错。

就像官方给出的示例代码一样:



from my_lib import Object
import os
from my_lib import Object3
from my_lib import Object2
import sys
from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14
import sys
from __future__ import absolute_import
from third_party import lib3

print("Hey")
print("yo")

上述的代码存在了多种 import 风格,比如同一模块下分别导入的部分各占一行、同一模块下导入的部分只站一行等;并且还存在重复导入的情况。因此要么我们就干脆每个导入的部分只占一行,要么就是来自同一模块下的导入部分都连接在一起。默认情况下 isort 会是后者:



from __future__ import absolute_import

import os
import sys

from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8,
                         lib9, lib10, lib11, lib12, lib13, lib14, lib15)

from my_lib import Object, Object2, Object3

print("Hey")
print("yo")

使用了 isort 之后它会将我们每个 .py 文件下中 import 部分的代码进行规整,同时它会按照内置库(及字母顺序)、第三方库以及本地自有模块的次序对我们所导入的部分进行排列。这样我们的代码不仅看起来清爽了许多,同时也能准确定位到我们已经导入的部分有哪些。

isort 不会像 black 那样「执拗」,而是也给你尽可能地预留了一些可供自定义的配置项,以便能满足不同风格或团队规范的需要。但对它的使用和 black 一样简单,只需要将其安装到我们的环境中后,直接调用即可:



isort <项目文件夹 / 源码文件夹 / .py 文件等>

静态检查工具

由于像 Python、JavaScript 这些动态类型的解释型语言,不具备 Java、Go 这样静态类型的编译型语言在编译期时的检查机制,因此当一个项目中的代码量过多时,就会造成「动态一时爽,重构火葬场」的局面。

尽管 Python 社区推行了 PEP484 提案并让 Python 拥有了类型注解的能力,但不正确的使用依旧是很难达到静态语言编译检查的那种效果。所以,一些静态类型检查工具也就应运而生,以便能及时发现我们代码中的问题

目前关于 Python 的静态检查工具有许多,知名的有那么几个:

  • pylint:PyQCA 团队维护,提供了高度的可配置项(这当然也是缺点),如果你有在 VS Code 编写过 Python 代码,那么有可能就会碰到让你安装 pylint 的情况;

  • flake8:仍是由 PyQCA 团队维护,它在功能上和 pylint 存在重叠的地方,但它也集成了 PEP8、McCabe 以及第三方插件等多个部分,功能上比 pylint 相对丰富一些;

  • mypy:由 Python 的缔造者 Guido van Rossum(Python 使用者们亲切将其称为龟叔)在 Dropbox 工作时所创建,可以说没有人比他更了解 Python 了,也可以算作是相对权威静态检查工具了;

  • pyright 由微软开源的、基于 TypeScript 和 JavaScript 编写,高度集成于 VS Code,对于经常使用 VS Code 来写代码的人来说或许是个比较好的选择;

  • pytype 由谷歌开源并维护,但相对于其他工具来说检查会较为宽松一些。

以上都是使用人数多的、并且质量都较为上乘静态检查工具,可以根据自己或团队的实际需要来选择;不过 flake8 和 mypy 通常都算是一个正规项目中的标配了,这里我就只以 flake8 为例。

和前面两个的工具一样,先通过 pip install flake8 命令安装之后再使用:



flake8 <项目文件夹 / 源码文件夹 / .py 文件等>

大部分情况下,flake8 会根据默认的一些规范对代码进行检查,默认是依照 pyflake 的错误(或违规)码表来进行检查,你可以在 flake8 官方 《Error/Violation Codes》 一章中浏览到完整的码表;当然我们也可以通过特定的命令行参数来指定检查的事项或忽略掉的事项:



flake8 --select E121 example.py

这里就只会选择检查 E121 这项来自于 pycodestyle 码表中的一项,如果代码导入的模块或包没有缩进或悬挂,那 flake8 就会在检查代码之后抛出信息:



example.py:5:9: E121 continuation line under-indented for hanging indent

这里就对应着 example.py 文件里的第 5 行到第 9 行代码,我们直接就可以根据 flake8 的结果精准修改检验不通过的部分即可。

工具链整合

上述说的几个工具,大部分情况都能通过 IDE、脚本、Git Hook 等途径得到有效整合。通常情况下我都会在每次提交代码到仓库前预先分别使用 isort、black 以及静态检查的 flake8。

由于 black 的情况会比较特殊,像 isort 这样的格式化工具处理过后的代码都会 black 再次格式化,因此 black 也给出了兼容其他工具的配置项共同选项,尽最大程度的和其他工具共存。根据 black 官方的建议,通常我们可以放在某些配置文件中,比如 pyproject.tomlsetup.cfg 等,内容如下:



multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
ensure_newline_before_comments = True
line_length = 88

同理,flake8 也是如此:



max-line-length = 88
extend-ignore = E203, W503

因此我们可以将以上内容都写在一个配置文件中,这里我选的是 setup.cfg 文件中:



[isort]
multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
ensure_newline_before_comments = True
line_length = 88

[flake8]
max-line-length = 88
extend-ignore = E203, W503

这样就能使得 black、isort 和 flake8 能够更好地协同工作;同时我们可以将其放在一个脚本文件中,方便每次运行调用,大致类似于:



# code_fmt.sh

WORKDIR=$PWD
isort $WORKDIR \
    && black $WORKDIR --skip-string-normalization \
    && flake8 $WORKDIR

当然我们也可以将其集成在我们的 IDE 中,比如在 VS Code 中,当我们安装好上述工具后,可以在设置项中关于 Python 的配置项里找到关于上述工具的配置栏目。这里我就以 black 为例:

图片

isort、flake8 的设置也是类似。

然后再次在设置中找到关于编辑器的设置,并勾选 Format on Save 的选项,这样在我们每次保存代码文件时,就会自动帮我们格式化:

图片


attachments-2021-04-APJgnR4x607d46b113b16.jpg

  • 发表于 2021-04-19 17:01
  • 阅读 ( 652 )
  • 分类:Python开发

0 条评论

请先 登录 后评论
小柒
小柒

1320 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1320 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章