编注:本文为栏目内「技能拓展」模块的试读文章。在开始正式更新后内容和形式可能会有所变动。


从以前一个不会敲代码的小白,到现在成为敲 Python 代码贼溜的「老鸟」的过程中,除了通过大量的学习、实践,让自己更容易写出 Pythonic(即理解为「很有 Python 范儿」之意)的代码外,大部分时间里还是多亏 Python 社区里许多好用的工具。在这些工具的辅助之下,配合 IDE 可以即时发现代码中的问题,从而提升编码水平。

对于大多数人来说,学习和实践新知识可能没有统一的定式,但在使用的工具上我们可以做到统一,统一的工具能让我们写出更规范、质量更好的代码。

因此无论对于刚入门的小白还是正在成长的新人来说,学会使用辅助工具是十分有必要的。本文将会介绍一些笔者在日常个人项目和工作中都会用到,并最后向团队推广使用的 Python 辅助工具。

代码格式化工具

随着 PEP 8 提案的出现,Python 社区也有了自己的一套关于 Python 代码的规范指导。类似这类的编程代码规范或指导在大多数编程语言中是很常见的,可能是来自于编程语言官方,也可能是来自于某个大厂的团队总结。比如 AirBnb 公司的 JavaScript 代码风格指南、来自于阿里巴巴的《阿里巴巴 Java 开发手册》 、JetBrains 官方所推崇的 Kotlin 编程规范 等。

但我相信,大多数人其实并不会条分缕析地去浏览或记下规范里的大多数内容,因为这些内容往往都过于琐碎。比如变量或方法以何种方式命名、代码缩进是用空格还是 Tab、用以表示代码块的标识是否独占一行……以至于在实际开发中我们很难做到去一边写代码,一边去检查我们是否合乎某个规范,常常需要和别人在 Code Review 的协作中发现并手动修改。

不过代码当中有一个部分,却是能被自动化并且通过工具来检查,最后帮助我们完成的修改的,即代码格式化(Codes Formatting)。

代码格式化其实旨在改善代码的可读性从而提高整体的代码质量,即可以理解成为我们在上学时,老师常常会提到的但却经常容易被人忽视的考试得分项:保持「卷面整洁」。

读者可以分别对比以下两段代码:

# without code formatting

def this_is_a_function_without_formatting(var_a, var_b, var_c, var_d, with_long_arguments):
    if var_a != 0 and var_b == 1 and not (var_c or var_d) and len(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])
# code formatting

def this_is_a_function_with_formatting(
    var_a,
    var_b,
    var_c,
    var_d,
    with_long_arguments,
):

    if (
        var_a != 0
        and var_b == 1
        and not (var_c or var_d)
        and len(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],
)

相比未格式化的代码,可以看到格式化后的代码更加规范、可读性更好。

而 Python 中就存在能实现这样风格的格式化工具。早期著名的格式化工具的有 autopep8 和 Google 的 yapf,但它们在实际过程中或多或少需要一些配置。

Black

受到来自于 Go 语言的 gofmt 工具启发(Go 语言官方推崇的强制性代码风格),一个新的格式化工具——Black——正在 Python 社区中变得逐渐流行。

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

比如说下面这一段代码(来源于 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 直到今年 2022 年的 1 月底才从测试版本跨越正式版本,但即便如此,早在它属于测试版时就已频繁被使用在一些 Python 主流或是社区重量级的开源项目中,比如 Pandas、SQLAlchemy、Pytest 等。

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

black /path/to/your/python/file.py

就是这么简单!

当然 Black 也提供了一些可供配置的选项,但工具本身的默认设置已经非常完善,不需要我们再额外调整了,需要调整的配置项也是寥寥几个,我们可以通过命令行来查看并使用对应的配置项:

$ black --help
Usage: black [OPTIONS] SRC ...

  The uncompromising code formatter.

Options:
  -c, --code TEXT                 Format the code passed in as a string.
  -l, --line-length INTEGER       How many characters per line to allow.
                                  [default: 88]
  -t, --target-version [py33|py34|py35|py36|py37|py38|py39|py310]
                                  Python versions that should be supported by
                                  Black's output. [default: per-file auto-
                                  detection]
# 各位可以在终端自行尝试,查看完整的参数列表和解释

也可以在项目文件夹下(即包含代码的目录中)通过配置文件来配置,一劳永逸,比如使用 Python 官方的 PEP 518 提案中的 pyproject.toml  文件来配置,选项与前面命令行上的选项参数名称一致:

# pyproject.toml

[tool.black]
line-length = 89
skip-string-normalization = true

之后在包含该配置文件的目录下,只需要执行 Black 命令以及待格式化的代码文件路径即可,而无须指定相应的命令行选项参数。

isort

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

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 的 Python 文件下中 import 部分的代码大致按照以下顺序并以字母排序进行规整:

  1. 内置的特殊标准库(或模块);
  2. 内置标准库或模块;
  3. 第三方库;
  4. 本地自有模块。

这样我们的代码不仅看起来清爽了许多,同时也能准确定位到我们已经导入的部分有哪些。

使用 isort 之前同样我们也需要通过 pip 命令进行安装:

pip install isort

然后用法和 Black 一样,在使用时指定需要格式化的 Python 代码文件路径即可:

isort /path/to/your/python/file.py

isort 不会像 Black 那样「执拗」且充满不妥协精神,它存在大量可供选择的配置选项和定制化接口,以满足不同风格、团队规范的需要,官方文档上有着详细的记录:

isort 同样可以将配置项写入配置文件中,并且也可以和 Black 搭配使用,不过需要指定相应的兼容配置项 profile 并将其设置为 black,这里笔者仅以 pyproject.toml 文件为例:

[tool.black]
line-length = 89

[tool.isort]
profile = "black"
src_paths = ["code/*", "src/*"]
multi_line_output = 3
verbose = true

这里除了 Black 的配置外,还额外添加了 isort 的配置,其中:

  • profile 参数设置为 black 表示兼容 Black 格式化风格;
  • src_paths 参数表示只格式化指定目录下的 Python 文件
  • multi_line_output 参数设置为 3 表示选择「Vertical Hanging Indent」的格式化风格
  • verbose 参数设置为 true 表示每次格式化时都输出详细信息。

以上配置项仅仅只是笔者在平时个人或工作项目中的配置示例,受限于篇幅读者可以根据自己的需要查阅 isort 的官方文档以了解更多的可配置项内容。

静态检查工具

由于像 Python、JavaScript 这些动态类型的解释型语言,不具备 Java、Go 这样静态类型的编译型语言在编译时的检查机制,因此我们无法事先感知到代码当中存在的一些问题,只有运行代码时这些问题才会被暴露。比如像如下的代码:

在 IDE 中此时这段代码并并没有出现任何问题,我们仅知道 multiply() 只有一个 numbers 参数,但到底这个参数可以传递哪些类型的数据我们却不得而知。于是当我们运行上述代码时,问题就出现了:

可以看到,multiply() 函数应该是只支持包含数值型的容器类型数据,因此当我们向其传入一个包含字符串型的集合时必然会出现错误。

这也就是动态类型的解释型语言存在的一个弊端,所以在开发中不少程序员都会体会到「动态一时爽,重构火葬场」的心路历程。

但如果当我们以类似静态类型的编译语言那样,事先声明参数以及结果的类型,那么能大概率在程序运行之初在 IDE 的辅助下事先发现代码中可能存在的问题。这里笔者对上述代码进行简单修改:

自从 PEP 484 提案开始,Python 代码已经逐渐开始支持这种声明类型的编程方式,它旨在能够让 IDE 或检查工具能据此发现代码中可能存在的问题,从而提升 Python 代码的质量以及程序的健壮性。

因此从结果可以看到,当我对代码简单修改并添加类型注解之后,IDE 很快速地就能发现了与代码要求不相符的部分,并对其给出了详细提示。

目前关于 Python 的静态检查工具有许多,总的来说可以分为两类:代码规范检查和代码逻辑检查。

代码规范检查工具

在前面关于 Black 的内容中笔者有曾提到过 PEP 8 规范,其中有部分的规范内容是可以通过像 Black 和 isort 这样的工具来实现自动格式化,而有些部分可能就需要借助代码规范检查工具才能发现并手动处理,比如一行代码是否超过 80 个字符、是否存在已导入未使用的库或模块等,这些都是需要代码规范检查工具介入的地方。

目前大部分 IDE 也具备了类似的代码规范检查功能,我们可以很轻易发现大部分不合乎代码规范的问题:

除此之外,Python 社区也有类似的工具供我们选择,大部分我们都可以在 PyQCA 的项目仓库中找到:

  • pylint:提供了高度的可配置项(这当然也是缺点),如果你有在 VS Code 编写过 Python 代码,那么有可能就会碰到让你安装 pylint 的情况;
  • pycodestyle:主要用于检查代码是否符合 PEP 8 规范;
  • pydocstyle:主要用于检查文档代码是否符合 PEP 257 规范;
  • flake8:它在功能上和 pylint 存在重叠的地方,但它也集成了 pycodestyle、 pyflakesmccabe 以及第三方插件等多个部分,功能上比 pylint 相对丰富一些;
  • ……

这里笔者就主要选择 flake8,因为它是多个工具的集合,在使用上也相对方便。在使用前先通过 pip 命令工具进行安装:

pip install flake8

之后我们可以直接像使用 Black 或 isort 那样在执行命令后紧接着对应的 Python 代码文件路径。比如前面的 IDE 代码:

# main.py

import os
import string

a=102

content = "this is a very long string contains: %s, %s" %(string.ascii_lowercase, string.ascii_uppercase)

if not(len(content)==0):
    if (
        (1+2) % (4
         +3)
    ) == 1 and a is not None:
        pass

使用 flake8 检查后得到的结果将会是这样:

$ flake8 main.py
main.py:1:1: F401 'os' imported but unused
main.py:4:2: E225 missing whitespace around operator
main.py:6:58: E225 missing whitespace around operator
main.py:8:20: E225 missing whitespace around operator
main.py:10:11: E226 missing whitespace around arithmetic operator
main.py:11:10: E128 continuation line under-indented for visual indent
main.py:11:11: E225 missing whitespace around operator
main.py:13:13: W292 no newline at end of file

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

$ flake8 --help
usage: flake8 [options] file file ...

positional arguments:
  filename

optional arguments:
  -h, --help            show this help message and exit
  -v, --verbose         Print more information about what is happening in flake8. This
                        option is repeatable and will increase verbosity each time it is
                        repeated.
  --output-file OUTPUT_FILE
                        Redirect report to a file.
  --append-config APPEND_CONFIG
                        Provide extra config files to parse in addition to the files found
                        by Flake8 by default. These files are the last ones read and so they
                        take the highest precedence when multiple files provide the same
                        option.
# 各位可以在终端自行尝试,查看完整的参数列表和解释

不过相比于 IDE 来说,我们还可以为 flake8 设置相应的配置文件,通过该文件可以配置选择忽略掉哪些检查项、排除哪些文件等。

我们只需要在包含代码文件的根文件路径中包含一个名为 .flake8 特殊命名的文件,然后在当中试着以如下方式填写相应的内容:

[flake8]
ignore =
    # F401 imported but unused
    F401,
    # E225 missing whitespace around operator
    E225,
    # E501 line too long
    E501,
    # W292 no newline at end of file
    W292

exclude =
    .git,
    __pycache__,
    dist

这等价于命令行:

flake8 --ignore F401,E225,E501,W292 \
    --exclude .git,__pycache__,dist \
    main.py

最后输出的结果将会比默认配置下输出的信息输出更为简洁:

$ flake8 main.py
main.py:10:11: E226 missing whitespace around arithmetic operator
main.py:11:10: W503 line break before binary operator
main.py:11:10: E128 continuation line under-indented for visual indent

然后我们只需要根据信息上所示找到对应的代码行数进行修改即可。

静态类型检查工具

由于 PEP 484 提案的出现,开始使得 Python 也可以像静态类型语言那样为变量、参数或函数返回值标注相应的类型,以便能配合 IDE 和相应的工具,共同发现代码当中可能存在的逻辑错误或是类型错误等问题,从而改善代码质量并提高程序健壮性。

总而来说,目前 Python 社区主要有这么以下几个流行的静态类型检查工具(Staic type checker),根据项目创建日期由远及近为:

  • mypy:由一位名叫 Jukka Lehtosalo 的软件工程师在 2012 年时在 Dropbox 开发,不久之后 Dropbox 内部陆陆续续也有几位开发者参与到该项目的开发中,当中就包括了这么一位大家耳熟能详的人物——Python 的缔造者——Guido van Rossum(国内的 Python 使用者亲切将其称为龟叔),可以说 mypy 经过这些年来的发展,已经是 Python 社区中久负盛名且文档最全的静态类型检查工具;
  • pyre:由 Facebook 公司所开源并维护,当中还包含了一个名为 Pysa 的静态分析工具,文档相对较少,使用 OCaml 编程语言实现;
  • pytype:由谷歌公司开源并维护,该工具相比于其他的静态类型检查工具而言较为宽松,不会对代码有较强的约束;
  • pyright:由微软开源,当中的部分功能被集成到了 VS Code 的 Pylance 插件中,可以在监听模式下运行,能够随时对我们正在编写的 Python 代码的进行检查。

这里笔者只以 mypy 为例,如果读者对于其他的静态类型检查工具有特别偏好,可以自行挑选并阅读官方文档后使用。使用前需要通过 pip 命令进行安装:

pip install mypy

简单来说,如果我们希望静态类型检查工具发挥作用,那么就最好编写带有类型注解的 Python 代码

这里我们仍然以前面没有添加类型注解的 multiply() 函数为例:

# main.py

def multiply(numbers):
    total = 1
    for number in numbers:
        total *= number
    return total


if __name__ == '__main__':
    multiply({"10", "20"})

如果我们没有为代码添加任何类型注解,那么使用 mypy 时将不会得到任何有效的信息提示:

$ mypy main.py
Success: no issues found in 1 source file

只有当我们为其添加上类型注解,才会发挥 mypy 应有的作用:

from typing import Sequence, Union

Numeric = Union[int, float]


def multiply(numbers: Sequence[Numeric]) -> Numeric:
    total = 1
    for number in numbers:
        total *= number
    return total


if __name__ == '__main__':
    multiply({"10", "20"})

结果如下:

$ mypy main.py
main.py:9: error: Incompatible types in assignment (expression has type "float", variable has type "int")
main.py:14: error: Argument 1 to "multiply" has incompatible type "Set[str]"; expected "Sequence[Union[int, float]]"
Found 2 errors in 1 file (checked 1 source file)

从结果可以看到,通过 mypy 的检查我们不仅能发现第十四行代码(即 __main__ 处的部分)传入了一个包含字符串的集合类型,却不是包含数值类型的 Sequence。除此之外,mypy 还提示了我们 multiply() 函数中定义的结果变量 total 类型与 int 类型可能存在非同类型的情况,即当我们传入的是包含浮点型的值时 total 将会在累乘的过程中被转换为浮点型。

因此针对上述问题我们可以手动预先修改,对于 total 变量,我们即可以为其添加和返回值一样的类型注解:

def multiply(numbers: Sequence[Numeric]) -> Numeric:
    total: Numeric = 1
    for number in numbers:
        total *= number
    return total

当然 mypy 也提供了一种让我们忽略掉可能被检查到并提示错误的机制,即我们可以通过注释来进行标注:

def multiply(numbers: Sequence[Numeric]) -> Numeric:
    total = 1
    for number in numbers:
        total *= number  # type: ignore
    return total

无论是使用以上哪种方式,最后我们都不会再得到 Incompatible types in assignment 的错误提示了:

$ mypy main.py
main.py:14: error: Argument 1 to "multiply" has incompatible type "Set[str]"; expected "Sequence[Union[int, float]]"
Found 1 error in 1 file (checked 1 source file)

类似这样的问题还有很多,但我们不能一一都通过编码来解决,这时就需要我们自己在配置文件中设定关于 mypy 的相关检查配置。

默认情况下 mypy 的检查是相对严格的,也会尽可能地发现 Python 代码中存在问题的部分;当然这些检查项都会有与之对应的配置项,因此我们如果并不需要那么多提示的检查或提示信息,那么也可以通过配置文件来进行预先设定:

# ref: https://mypy.readthedocs.io/en/stable/config_file.html#example-pyproject-toml

# mypy global options:

[tool.mypy]
python_version = "2.7"
warn_return_any = true
warn_unused_configs = true
exclude = [
    '^file1\.py$',  # TOML literal string (single-quotes, no escaping necessary)
    "^file2\\.py$",  # TOML basic string (double-quotes, backslash and other characters need escaping)
]

# mypy per-module options:

[[tool.mypy.overrides]]
module = "mycode.foo.*"
disallow_untyped_defs = true

[[tool.mypy.overrides]]
module = "mycode.bar"
warn_return_any = false

[[tool.mypy.overrides]]
module = [
    "somelibrary",
    "some_other_library"
]
ignore_missing_imports = true

关于 mypy 的所有配置选项与说明,读者可以自行查阅官方文档的配置部分

工具链整合

到本小节为止,本章所提及的工具多以命令行的形式使用。但相信有不少和我一样的「懒人」,希望在尽可能不敲命令行的情况下,也能使用到这些工具以帮助我们改善或提高 Python 代码的质量。

因此最好的方式就是我们能将其整合到一块,然后只需要在每次完成代码时在统一运行、调用即可。

选择统一的配置文件

大部分工具或多或少都会存在可供配置的选项,因此整合这些工具配置项的最好方式就是将其全都放在一个配置文件中,比如 pyproject.toml

[tool.black]
line-length = 88

[tool.isort]
profile = "black"
src_paths = ["code/*", "src/*"]
multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
ensure_newline_before_comments = True
line_length = 88

[tool.mypy]
# mypy optional settings here.
# ...

[tool.pytest]
# pytest optional settings here.
# ...

pyproject.toml 出现得较晚,所以可能会存在部分工具仍不支持以这种方式配置的情况(比如 flake8),因此我们也可以用 setup.cfg 这一 Python 项目特有的构建配置文件来作为兼容较早工具的选择,语法上大同小异:

; setup.cfg

[black]
line-length = 88

[isort]
profile = "black"
src_paths = ["code/*", "src/*"]
multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
ensure_newline_before_comments = True
line_length = 88

[mypy]
; mypy optional settings here.
; ...

[pytest]
; pytest optional settings here.
; ...

注:由于 flake8 目前尚未支持 pyprojec.toml 文件,所以在设定 flake8 的选项时要么将其配置项单独放置于一个名为 .flake8 的文件中,要么将其配置项放置于 setup.cfg 中。

统一执行的方式

像 Black 和 isort 这样能够实时将代码格式化的工具,我们可以将其整合到开发或编写代码所用的 IDE 工具中,这样就可以在每次保存代码时自动进行格式化,而不需要我们手动运行。

这里笔者只以常见的 VS CodePycharm 为例。

VS Code

在 VS Code 中当我们安装了微软官方的 Python 插件时,默认就可以在用户设置选项中搜索到关于格式化的有关设置,然后我们只需要:

  1. 搜索 python.formatting.provider 设置修改成 black 选项;
  2. python.formatting.blackPath 设置中添加 Black 工具完整的程序路径即可(isort 的设置也是类似);
  3. 如有相应的一些 Black 配置选项,也可以在 python.formatting.blackArgs 设置中进行添加。

之后我们再另外勾选 VS Code 中 editor.formatOnSave 选项让代码在保存时自动格式化:

并且搜索 editor.codeActionsOnSave 设置,然后添加这一行设置 "source.organizeImports": true,最后会配置如下所示:

{
    "editor.codeActionsOnSave": {
        "source.organizeImports": true
    }
}

上述所有设置除了使用 UI 界面进行设置以外,也可以通过 JSON 文件进行配置,配置内容如下:

{
  "editor.formatOnSave": true,

  "python.formatting.provider": "black",
  "python.formatting.blackPath": "<your-black-execute-file-path-here>",
  "python.formatting.blackArgs": ["your", "black", "arguments", "here"],

  "python.sortImports.path": "<your-isort-execute-file-path-here>",
  "editor.codeActionsOnSave": {
    "source.organizeImports": true
  }
}

Pycharm

Pycharm 的配置方式和 VS Code 的配置方式大体上都相同,只不过由于 Pycharm 没有直接内置类似 VS Code 设置中的有关于文件操作的功能,因此需要额外借助一个名为 File Watchers 的插件来实现,这里以 Black 的设置为例(isort 也是类似):

注:本小节演示的是 Pycharm Community 社区版,而 Pycharm Professional 专业付费版已经内置了 File Watchers,可以无需安装。

安装完成后我们就需要接着在 Pycharm 的设置页中找到 File Watchers 的设置项,并在当中完成设置:

设置完成之后,我们每次只需要保存代码时就会自动格式化。

编写命令行脚本

除了与 IDE 整合这样的方式之外,我们还可以将所有工具的执行命令放在一个脚本文件中,每次只需要调用该脚本文件即可执行所有命令。

比如说 Shell 脚本:

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

又比如使用 Makefile 文件并搭配 make 构建命令:

.PHONY: all fmt check

WORKDIR := .

fmt:
    @echo "formatting code..."
    @isort -v $(WORKDIR)
    @black -v --skip-string-normalization $(WORKDIR)
    @echo "formatting code done."

check:
    @echo "checking code..."
    @flake8 $(WORKDIR)
    @mypy --strict $(WORKDIR)
    @echo "checking code done."

all: fmt check

除前面的几种方式外,还可以通过类似于 Git Hookspre-commitGithub Actions 等多种方式,在每次提交代码时完成上述格式化以及代码检查相关的工作,适用于比较专业且需要团队协作的场景中,受限于篇幅本文就不一一展开,感兴趣的读者可以自行了解。

结尾

本文主要介绍了一些能够提升 Python 代码质量的工具,比如代码格式化工具、静态检查工具等。

当然不仅是 Python,目前主流的编程语言都会有许多配套的工具链或类似的设施。

通过对这些工具的使用一方面可以提高我们代码的可读性与质量,另一方面能够及早发现当中可能存在的某些代码问题从而整体提高程序的健壮性。


参考文档