编辑
2025-07-08
Python
00

目录

Python 代码标准化工程实践
pre-commit
isort
核心作用:
手动使用方式:
配置最佳实践:
Pre-commit 集成方式:
徽章:
冲突解决:
Black
核心作用:
手动使用方式:
格式化效果对比:
格式化前
格式化后 (by black)
**配置最佳实践:
Pre-commit 集成方式:
徽章:
mypy
核心作用:
手动使用方式:
配置最佳实践:
Pre-commit 集成方式:
徽章:
冲突解决:
mypy vs. Pylance (Pyright)
bandit
核心作用:
手动使用方式:
配置最佳实践:
Pre-commit 集成方式:
徽章:
冲突解决:
autoflake
核心作用:
效果对比
清理前 (autoflake)
清理后 (autoflake)
配置最佳实践:
Pre-commit 集成方式:
徽章:
冲突解决:
pyupgrade
效果对比
升级前 (pyupgrade)
升级后 (pyupgrade --py38-plus)
pydocstringformatter
核心作用:
手动使用方式:
配置最佳实践:
Pre-commit 集成方式:
徽章:
冲突解决:
ruff
核心作用:
手动使用方式:
Pre-commit 集成方式:
**徽章:
冲突解决:
完整配置与最佳顺序
推荐的检查顺序与理由
完整 .pre-commit-config.yaml 示例
总结:构建高效的代码质量工作流
工具应用阶段划分
核心建议

Python 代码标准化工程实践

在现代 Python 项目中,使用多种工具来自动化代码风格检查、格式化和静态分析是一种常见的最佳实践。这些工具可以在代码提交前捕获问题,确保代码库的一致性和质量。

pre-commit

pre-commit 是一个用于管理 Git 预提交钩子(pre-commit hooks)的框架。它允许开发者在每次代码提交前自动运行一系列检查,从而充当代码质量的第一道防线[gatlenculp.medium.com]

通过 .pre-commit-config.yaml 文件配置需要启用的钩子列表,pre-commit 会在提交时依次执行这些钩子脚本或命令。如果任意检查失败,提交将被拒绝,提示开发者修复问题[dev.to]

核心作用: pre-commit 本身不直接进行代码检查或格式化,而是管理其他工具的集成。它支持众多插件和钩子,能够与各类代码检查器、格式化工具和静态分析工具配合使用[dev.to]。其作用在于确保代码在提交前符合项目设定的规范,例如统一代码风格、捕获语法错误或潜在安全漏洞等,从而提升团队协作效率和代码质量。

手动使用方式: 首先安装 pre-commit:

shell
pip install pre-commit

然后在项目根目录创建 .pre-commit-config.yaml 文件,列出需要启用的钩子。例如:

yaml
repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - repo: https://github.com/psf/black rev: 24.3.0 hooks: - id: black args: [--line-length=88]

安装 Git 钩子脚本:

shell
pre-commit install

之后,每次执行 git commit 时都会触发配置的钩子检查。也可以手动运行所有钩子检查:

shell
pre-commit run --all-files

如果不需使用可以执行以下命令进行hooks卸载:

pre-commit clean

配置最佳实践:.pre-commit-config.yaml 中,为每个钩子指定稳定的版本(rev)以确保一致性。可以通过 args 传递工具的额外参数,例如设置代码行长度等。对于不需要每次提交都检查的文件,可以使用 exclude 正则排除。常用的钩子包括:

  • trailing-whitespace:检查并去除行尾多余空格。
  • end-of-file-fixer:确保文件以换行符结尾。
  • check-yaml:校验 YAML 文件格式。
  • check-added-large-files:防止大文件被加入版本控制。
  • black:代码格式化工具。
  • isort:导入排序工具。
  • flake8:代码风格和错误检查。
  • mypy:类型检查。
  • bandit:安全漏洞扫描等。

通过合理组合这些钩子,可以在提交前自动执行代码格式化、风格检查、类型检查和安全扫描等操作,保证代码库的规范和质量[gatlenculp.medium.com]

Pre-commit 集成方式: 由于 pre-commit 本身就是管理预提交钩子的工具,集成其他工具通常是在 .pre-commit-config.yaml 中添加对应钩子的仓库和 ID。例如,集成 black 格式化工具的配置片段如下:

yaml
- repo: https://github.com/psf/black rev: 24.3.0 # 指定版本 hooks: - id: black args: [--line-length=88] # 传递参数

更多钩子的配置可以参考 [pre-commit.com] 官方文档和钩子仓库。

徽章: pre-commit 项目本身提供徽章,可在 README 中显示项目已启用 pre-commit 钩子:

pre-commit

markdown
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)

将其添加到项目 README 以表明代码提交前会自动运行检查。

冲突解决: pre-commit 框架本身一般不会与其他工具冲突,因为它只是调用其他工具。但需要注意钩子的执行顺序和配置。例如,如果多个钩子都修改代码(如格式化工具和导入排序工具),应确保它们按正确顺序执行,避免相互覆盖。通常先运行修改代码的钩子(如格式化、排序),再运行只检查不修改的钩子(如风格检查、类型检查),以确保前者的修改能被后者正确验证。此外,不同工具的配置应保持一致(例如行宽限制),防止同一问题在不同钩子中出现矛盾提示。通过合理配置钩子顺序和参数,可以避免大多数冲突。

isort

isort 是一个用于自动排序 Python 导入语句的工具。它可以将导入按字母顺序排列,并自动按类型和模块来源进行分组(例如标准库、第三方库、本地应用库等)isort官网。使用 isort 可以使代码中的 import 语句保持一致的顺序和结构,提高可读性。

核心作用:

isort 的主要功能是规范导入顺序。在大型项目中,手动维护导入的顺序容易出错且耗时,isort 则可以自动完成这一任务。通过将导入按标准库、第三方库、本地模块等分类排序,isort 确保代码中的导入块结构清晰、易于浏览。

手动使用方式:

安装 isort:

shell
pip install isort

对指定文件或目录运行 isort 进行导入排序:

shell
isort myfile.py # 格式化单个文件 isort mypackage/ # 递归格式化整个包

默认情况下,isort 会直接修改文件内容。可以使用 --check-c 参数仅检查不修改,此时 isort 会输出不符合排序规范的文件列表。也可以使用 --diff 查看排序前后的差异而不实际修改文件。

配置最佳实践:

isort 支持通过 pyproject.toml.isort.cfg 等文件进行配置。在 pyproject.toml 中,推荐将配置放在 [tool.isort] 部分,并添加注释解释关键配置项的作用。以下是一个示例:

toml
[tool.isort] profile = "black" # 采用与 Black 兼容的配置文件 line_length = 88 # 导入行最大长度(与 Black 一致) multi_line_output = 3 # 多行导入的输出格式(3表示括号换行) include_trailing_comma = true # 为多行导入添加尾逗号(减少 git diff) force_grid_wrap = 0 # 不强制网格换行 use_parentheses = true # 总是使用圆括号包裹多行导入 ensure_newline_before_comments = true # 在导入后注释前添加空行

配置说明:

  • profile = "black":使用 isort 内置的 black 配置文件,使 isort 的风格与 Black 格式化器兼容[pycqa.github.io]。这会应用一组优化的默认值(例如导入后添加尾逗号等)以减少与 Black 的冲突。
  • line_length = 88:设置导入语句的最大行宽,通常与代码格式化工具(如 Black)的行宽一致,避免格式化后换行不一致。
  • multi_line_output = 3:指定多行导入时的输出样式(0-12种选项)。值为3表示使用圆括号将导入项换行排列(类似 Black 风格)[pycqa.github.io]
  • include_trailing_comma = true:在多行导入的最后一项后添加逗号。这有助于生成更简洁的 git diff(新增或删除导入时只有一行变化)[pycqa.github.io]
  • force_grid_wrap = 0:设置为0表示当导入项超过行宽时,isort 会自动决定换行方式,而不强制特定列数的网格换行。
  • use_parentheses = true:强制所有多行导入使用圆括号包裹,而不是反斜杠续行。这与 Black 的风格一致,提高可读性。
  • ensure_newline_before_comments = true:确保在导入语句后的注释前有一个空行,以符合 PEP 8 对注释与代码间距的要求。

通过上述配置,isort 将按照 Black 兼容的风格对导入进行排序和格式化,减少与代码格式化工具的冲突。如果需要进一步调整,isort 还提供丰富的选项,例如自定义导入分组、忽略特定文件等,可参考 [pycqa.github.io] 官方文档。

Pre-commit 集成方式:

.pre-commit-config.yaml 中添加 isort 钩子:

yaml
- repo: https://github.com/PyCQA/isort rev: 5.13.2 # 使用稳定版本 hooks: - id: isort args: [--profile=black] # 传递与 Black 兼容的配置

这样每次提交前,isort 都会自动检查并排序导入。如果导入需要调整,isort 会修改文件并终止提交,提示重新提交修改后的内容。

徽章:

isort 提供项目徽章,可在 README 中展示项目使用 isort 管理导入:

Imports: isort

markdown
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)

将其添加到 README 以表明代码导入由 isort 自动管理。

冲突解决:

isort 通常与代码格式化工具(如 Black)配合良好。但需注意两者的配置应协调:建议使用 isort 的 profile = "black" 配置,以确保 isort 生成的导入格式与 Black 兼容isort配置指南。如果不这样做,Black 可能会在 isort 排序后再次修改导入行的格式(例如添加括号或换行),导致不必要的改动。通过统一行宽和风格配置,可以避免 isort 与 Black 之间的冲突。另外,isort 与类型检查器 mypy 等工具通常没有直接冲突,可并行使用。

Black

Black 是一款代码格式化工具。它以严格统一的规则自动格式化代码,使团队无需在代码风格上争论不休black官方文档。Black 会重新排版代码,包括行长度、缩进、空格、括号位置等所有细微格式,确保代码风格一致。使用 Black 后,开发者将把控制权交给工具,不再手动调整格式细节,从而节省时间和精力。

核心作用:

Black 的核心功能是强制统一代码风格。它严格按照自己的规则格式化代码,消除人为的格式差异。这带来的好处包括:

  • 更快的代码审查(差异只反映代码逻辑变化)
  • 确定性的格式结果(相同输入多次格式化结果一致)
  • 摆脱格式规范的烦恼

Black 支持的风格在大多数情况下符合 PEP 8 规范,但在某些细节上有自己的偏好(例如默认行宽88字符、括号换行等)。通过使用 Black,团队可以确保代码库的格式高度一致,提升可读性和可维护性。

手动使用方式:

安装 Black:

shell
pip install black

对指定文件或目录运行 Black 进行格式化:

shell
black myfile.py # 格式化单个文件 black mypackage/ # 递归格式化整个包

Black 默认会直接修改文件内容。可以使用 --check-c 参数仅检查不修改,此时若文件不符合 Black 格式会返回非零状态并列出文件。使用 --diff 可以查看格式化前后的差异而不修改文件。

格式化效果对比:

Black 会对代码进行多方面的重新排版。以下是一些常见的格式化前后对比:

格式化前

# a_messy_file.py def my_function(arg1, arg2, arg3,arg4, long_argument_name_for_testing): result = {'key1': 'value1', 'key2' : 'value2', 'key3':'value3'} if (arg1 > arg2 and arg3 < arg4): print("Condition met") return result

格式化后 (by black)

# a_messy_file.py def my_function( arg1, arg2, arg3, arg4, long_argument_name_for_testing ): result = { "key1": "value1", "key2": "value2", "key3": "value3", } if arg1 > arg2 and arg3 < arg4: print("Condition met") return result
  • 行长度与换行: Black 会将过长的行自动换行,并优先使用括号而非反斜杠续行。例如:

    python
    # 格式化前 if some_long_condition1 \ and some_long_condition2: do_something() # 格式化后(Black) if ( some_long_condition1 and some_long_condition2 ): do_something()

    Black 移除了反斜杠,改用圆括号包裹条件,并在运算符前换行,使逻辑更清晰。

  • 空格与逗号: Black 会在二元运算符周围添加空格,在逗号后添加空格,并移除不必要的空格。例如:

    python
    # 格式化前 x=5 a = [1 ,2 ,3] func( 'hello' , 'world' ) # 格式化后(Black) x = 5 a = [1, 2, 3] func('hello', 'world')
  • 字符串引号: Black 统一将字符串引号改为单引号(除非字符串中包含单引号需要转义):

    python
    # 格式化前 message = "Hello, world!" # 格式化后(Black) message = 'Hello, world!'
  • 多行结构与尾逗号: 对于列表、元组、字典等结构,若拆分成多行,Black 会在每行元素后添加逗号,并将闭合括号单独成行:

    python
    # 格式化前 my_list = [1, 2, 3, 4, 5, 6] my_dict = { 'a': 1, 'b': 2 } # 格式化后(Black) my_list = [1, 2, 3, 4, 5, 6] # 单行容纳则合并 my_list = [ 1, 2, 3, 4, 5, 6, ] # 若单行放不下则每个元素一行并加尾逗号 my_dict = { 'a': 1, 'b': 2, }

    添加尾逗号和独立闭合括号的做法减少了版本控制中的差异(新增或删除元素时只有一行变化)

  • 导入语句: Black 会调整导入语句的格式,例如长的 from ... import 会换行并使用括号:

    python
    # 格式化前 from mypackage import (a, b, c, d, e, f) # 格式化后(Black) from mypackage import ( a, b, c, d, e, f, )

    这种格式与 isort 配合良好,特别是在 isort 启用了尾逗号选项时

通过以上示例可以看出,Black 的格式化使代码变得整洁统一。虽然初次使用可能会对代码进行大量修改,但此后每次提交的格式差异将降至最低,代码审查将更关注逻辑而非风格

**配置最佳实践:

** Black 是一款高度 opiniated(有主见)的工具,它的配置选项非常有限,目的是鼓励开发者接受其统一风格black代码风格指引。不过,Black 允许通过 pyproject.toml 等方式进行少量配置。推荐在 [tool.black] 部分设置必要的选项并添加注释说明:

toml
[tool.black] line-length = 88 # 代码行最大长度(默认88,符合大多数项目) target-version = ['py310'] # 目标支持的Python最小版本(可选) include = '\.pyi?$' # 需要格式化的文件正则(默认已包含.py,这里示例包含.pyi) exclude = ''' /( \.git/ # 排除Git目录 | \.hg/ # 排除Mercurial目录 | \.mypy_cache/ # 排除mypy缓存 | \.tox/ # 排除tox目录 | \.venv/ # 排除虚拟环境 | _build/ # 排除构建目录 | buck-out/ | build/ | dist/ )/ ''' # 需要排除的目录正则

配置说明:

  • line-length = 88:设置代码行的最大字符数。Black 默认使用88字符,比传统的80字符稍宽,被认为在可读性和减少换行之间取得了良好平衡。除非项目有特殊要求,否则不建议更改此值,以与社区习惯保持一致。
  • target-version = ['py310']:指定项目支持的最低 Python 版本。Black 会根据此选项调整格式化行为(例如是否使用新的语法特性)。如果项目兼容多个版本,可列出如 ['py38', 'py39', 'py310']
  • includeexclude:这两个参数分别用正则表达式指定需要和不需要格式化的文件路径。include 默认已包含所有 .py 文件,除非需要额外包含其他类型文件(如 .pyi 存根文件),否则一般不需要修改。exclude 用于排除某些目录或文件,如版本控制系统目录、虚拟环境、构建输出等,避免工具对这些文件进行不必要的格式化。上述示例中的正则排除了常见的版本控制和缓存目录。

Black 不支持配置诸如缩进风格(始终4空格)、运算符空格等细节,因为这些都由其严格的风格规则统一处理。这种极简配置哲学确保了无论项目新旧,格式化结果都一致可控。

Pre-commit 集成方式:

.pre-commit-config.yaml 中添加 Black 钩子:

yaml
- repo: https://github.com/psf/black rev: 24.3.0 # 使用稳定版本 hooks: - id: black args: [--line-length=88] # 可传递配置参数,如行宽

这样每次提交前,Black 都会自动格式化代码。若代码需要格式化,Black 会修改文件并阻止提交,提示重新提交修改后的内容。

徽章:

Black 提供项目徽章,可在 README 中展示项目使用 Black 格式化代码:

Code style: black

markdown
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

将其添加到 README 以表明代码风格由 Black 统一管理。

冲突解决: Black 由于其严格的格式化规则,有时会与其他代码风格工具产生“冲突”,但多数情况下这种冲突是可以调和的:

  • 与 isort 的配合: Black 会重新格式化导入语句的格式(如换行、括号),而 isort 负责排序和分组。为避免两者反复修改同一部分,建议使用 isort 的 --profile=black 配置,使 isort 输出的格式符合 Black 风格。这样 Black 通常不会再对 isort 排序后的导入做额外改动。此外,在 pre-commit 钩子中,应先运行 isort 再运行 Black,因为 isort 可能调整导入顺序,Black 最后统一格式可以确保最终输出符合规范。
  • 与 Flake8 等检查器的配合: Black 格式化后的代码在大多数情况下是符合 PEP 8 的,但某些规则(例如行宽、行末逗号等)Black 有自己的处理方式。如果项目同时使用 Flake8 检查风格,需要配置 Flake8 忽略那些由 Black 自动处理的规则。例如,Black 会处理行宽问题,因此 Flake8 可以忽略 E501(行过长)警告;Black 会在二元运算符前换行,这可能触发 Flake8 的 E226 等警告,也应在 Flake8 配置中忽略这些特定错误码。通过调整检查器的规则,可以避免对 Black 已处理的格式问题重复报错。
  • 与自定义格式规则的冲突: 由于 Black 不支持很多细粒度配置,如果项目有某些特殊格式要求无法被 Black 满足,可能需要权衡。例如 Black 不支持在字典字面量的逗号后不加空格的风格,如果项目希望如此就无法实现。这种情况下要么接受 Black 的风格,要么不使用 Black。但总体而言,Black 已被广泛接受为 Python 的事实标准格式化工具,与其规则保持一致利大于弊。

总之,通过合理配置和调整其他工具的规则,Black 可以与多数常用工具和平共处。其带来的统一风格和效率提升通常远大于解决这些小冲突的成本。

mypy

mypy 是 Python 的静态类型检查器。它可以分析带有类型注解的 Python 代码,验证类型使用是否正确,帮助捕获潜在的类型错误官方文档。通过在开发过程中运行 mypy,开发者能够在不实际运行代码的情况下发现类型不匹配、错误的返回类型等问题,提高代码的健壮性和可维护性。

核心作用:

mypy 的核心功能是静态类型检查。它读取代码中的类型提示(type hints),并在编译(或运行 mypy)时检查这些类型是否一致。例如,当函数期望接收一个整数参数但实际传入字符串时,mypy 会报告类型错误。这种检查有助于在早期发现因类型误用导致的 bug,减少运行时异常。此外,mypy 还支持类型推断,即使没有显式注解,它也能推断变量类型并进行检查。通过使用 mypy,团队可以逐步为代码添加类型注解,构建类型安全的代码库,提高代码质量和可读性

手动使用方式:

安装 mypy:

shell
pip install mypy

对指定文件或模块运行 mypy 检查:

shell
mypy myfile.py # 检查单个文件 mypy mypackage/ # 递归检查整个包

mypy 会输出检查结果,列出发现的类型错误或警告。如果没有错误则输出为空并返回状态0。可以使用 --check-untyped-defs 等选项增加检查严格程度。在持续集成环境中,通常会将 mypy 作为构建步骤之一,确保类型检查通过。

配置最佳实践:

mypy 的配置选项非常丰富,可通过 pyproject.toml(在 [tool.mypy] 下)或 mypy.ini 文件进行设置。以下是一个推荐的 pyproject.toml 配置片段,包含关键选项及注释说明:

toml
[tool.mypy] strict = true # 启用严格模式,打开所有推荐的检查选项 show-error-codes = true # 在输出中显示错误代码,便于针对性忽略 warn-unused-ignores = true # 警告那些未生效的# type: ignore注释 disallow-any-generics = true # 不允许泛型类型参数为Any disallow-subclassing-any = true # 不允许继承自Any类型 disallow-untyped-calls = true # 不允许调用未标注类型的函数 disallow-untyped-defs = true # 不允许定义未标注返回值和参数类型的函数 disallow-incomplete-defs = true # 不允许函数部分参数未标注类型(在strict模式下已包含) disallow-untyped-decorators = true # 不允许装饰器修饰未完全标注的函数 no-implicit-optional = true # 不将未显式Optional的None参数推断为可选 warn-redundant-casts = true # 对冗余的类型转换发出警告 warn-unused-configs = true # 对配置中未使用的选项发出警告 allow-redefinition = true # 允许变量重新定义(某些动态代码需要) cache-dir = ".mypy_cache" # 指定mypy缓存目录,避免污染项目根目录

配置说明:

  • strict = true:启用严格模式。这会打开一系列推荐的检查选项,包括 disallow-untyped-defsno-implicit-optionalwarn-redundant-casts 等,最大程度保证类型安全。对于新项目或希望逐步严格化的项目,建议从严格模式开始。如果代码库类型注解尚不完整,严格模式可能报错较多,可以考虑先使用 strict = false 并按需启用个别选项,待逐步补全注解后再切换到严格模式。
  • show-error-codes = true:让 mypy 在错误信息中显示错误代码(如 error: Incompatible return value type [1] 中的 [1])。这有助于识别具体是哪类错误,并方便在配置或代码中针对某些错误码进行忽略或调整。
  • warn-unused-ignores = true:当代码中存在 # type: ignore 注释但实际上没有需要忽略的错误时,mypy 会给出警告。这可以防止不必要的 ignore 堆积,保持代码整洁。
  • disallow-any-generics = true:禁止在泛型类型(如 List, Dict 等)中使用 Any 作为类型参数。这确保泛型的类型参数都是具体的,提高类型检查的有效性。
  • disallow-subclassing-any = true:不允许创建继承自 Any 的类型。这可以防止滥用 Any 作为基类的不良设计。
  • disallow-untyped-calls = true:不允许调用未进行类型标注的函数。如果调用了一个没有参数和返回类型注解的函数,mypy 将报错。这鼓励为所有函数添加类型提示,提高代码可读性和可维护性。
  • disallow-untyped-defs = true:不允许定义未标注类型的函数(即函数没有参数类型或返回类型注解)。这在严格模式下默认开启,要求所有函数都有完整的类型注解。对于存量代码,可能需要逐步补全注解或暂时使用 # type: ignore 忽略,但长远看应尽量满足此要求。
  • no-implicit-optional = true:禁止隐式的可选类型。当函数参数默认值为 None 时,要求显式使用 Optional[T] 注解类型,而不是由 mypy 推断为可选。这使代码意图更明确。
  • warn-redundant-casts = true:对冗余的类型转换发出警告。例如,如果变量已经是 int 类型,又显式转换为 int,mypy 会提示这可能没必要。这有助于保持代码简洁。
  • allow-redefinition = true:允许变量在同一作用域内重新定义。默认情况下,mypy 会对同一变量多次赋值不同类型的情况报错。设置此项为 true 可以关闭该检查,这在某些动态代码或重构场景下有用。但需谨慎使用,以免掩盖潜在的类型问题。
  • cache-dir = ".mypy_cache":指定 mypy 存储缓存的目录。mypy 会缓存分析结果以加速后续运行,默认缓存目录是 .mypy_cache,可以通过此选项显式指定并建议将其加入 .gitignore

以上配置提供了一个严格但实用的 mypy 检查环境。如果希望进一步调整,mypy 还有许多其他选项,例如 ignore-missing-imports(忽略缺失导入的错误)、strict-equality(启用严格的相等比较检查)等,可以根据项目需要启用。

Pre-commit 集成方式:

.pre-commit-config.yaml 中添加 mypy 钩子:

yaml
- repo: https://github.com/pre-commit/mirrors-mypy rev: v1.14.0 # 使用与项目兼容的mypy版本 hooks: - id: mypy args: [--strict] # 传递严格模式参数 additional_dependencies: [pydantic==2.0] # 可选:如果项目依赖某些类型库,需要在此列出,mypy才能识别

由于 mypy 需要项目的依赖环境来解析类型(例如第三方库的类型存根),在 pre-commit 中集成时要确保虚拟环境中安装了所有依赖。如果某些依赖在钩子中不可用,可以使用 additional_dependencies 列出它们,pre-commit 会在运行 mypy 前安装这些包。这样每次提交前,mypy 都会检查代码的类型一致性,发现问题会阻止提交。

徽章:

Checked with mypy

[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)

冲突解决:

mypy 作为静态类型检查器,通常与其他代码格式化或风格检查工具没有直接冲突。但需要注意以下几点:

  • 与代码格式化工具: mypy 不关心代码的格式细节,因此与 Black、isort 等不会互相干扰。格式化工具可能会移动代码行或重排结构,但只要代码逻辑和类型注解不变,mypy 仍会正确检查。需要确保的是,格式化工具不会意外破坏类型注解的语法(例如 Black 不会修改有效的类型注释)。实际上,Black 支持格式类型注释(如函数签名中的类型)并保持其正确语法。因此,mypy 和格式化工具可以安全地并行使用。
  • 与动态类型代码: 如果项目中存在大量动态类型的代码(无类型注解或使用 Any 类型),mypy 可能不会捕获很多问题。这并非冲突,而是需要逐步改进代码的类型覆盖。可以通过 mypy 的配置选项(如 disallow-untyped-* 系列)来逐步强制添加注解。在这个过程中,可能会与开发者的工作习惯产生“冲突”,需要团队达成共识,逐步提升类型安全。
  • 与类型存根和第三方库: mypy 依赖类型信息来检查。如果项目使用的某些第三方库没有提供类型存根(.pyi 文件),mypy 可能会报错 Need type annotation for 'xxx' 等。这可以通过安装该库的类型包(通常名称类似 types-xxx)或使用 # type: ignore 临时忽略。如果团队希望严格检查,应尽量为依赖库找到或提供类型存根,而不是长期忽略。这属于项目依赖管理和类型生态的问题,不是工具本身的冲突。

总的来说,mypy 与其他工具配合良好。其带来的静态类型检查能力能够有效提升代码质量,与格式化、风格检查工具一起构成完整的代码质量保障体系。

mypy vs. Pylance (Pyright)

在现代 Python 开发中,尤其是在 VSCode 环境下,开发者经常会遇到 mypy 和 Pylance(其底层是微软的 Pyright 类型检查器)的选择问题。它们并非完全的替代关系,而是各有侧重,组合使用效果更佳。

  • Pylance (基于 Pyright): 作为 VSCode 的语言服务器,其最大优势在于性能和实时交互性。它能提供极速的悬停类型提示、智能的代码补全和几乎瞬时的错误高亮。Pylance 非常适合在编码过程中提供即时反馈,提升开发效率。
  • mypy: 是一个更经典、可配置性更强的命令行工具。它拥有更广泛的社区支持和插件生态(如 Django-mypy)。mypy 通常被用作一个更正式、更严格的检查关卡,例如在 pre-commit 或 CI/CD 流水线中,确保代码在合入主分支前通过最全面的类型检查。

选择建议: 推荐组合使用。在 VSCode 中依赖 Pylance 获得流畅的实时开发体验,同时在 pre-commit 流程中集成 mypy 作为最终的代码质量门禁。这种方式结合了 Pylance 的速度和 mypy 的严格性与全面性。

PyCharm 内置了基于其自身引擎的类型检查功能。在 VSCode 中,Pylance 扩展提供了顶级的类型检查体验,同时也可以通过配置集成 mypy 作为额外的 linter。

bandit

bandit 是 Python 代码的安全漏洞静态分析工具。它通过解析代码的抽象语法树(AST),查找常见的安全隐患和编码错误官方文档。bandit 内置了一系列安全规则(插件),可以检测如硬编码的密码、危险的函数调用、不安全的加密实践等问题,帮助开发者在早期发现潜在的安全漏洞。

核心作用:

bandit 的核心功能是代码安全扫描。它充当一个“安全检查器”,在代码中寻找可能导致安全问题的模式。例如,bandit 会识别出使用 eval()pickle 反序列化、硬编码的密钥等高危操作,并给出警告。通过在开发流程中集成 bandit,可以在代码提交或部署前捕获常见的安全缺陷,防止将高危代码引入生产环境。bandit 尤其适用于大型项目或对安全性要求较高的项目,能够有效提升代码的安全合规性。

手动使用方式:

安装 bandit:

shell
pip install bandit

对指定文件或目录运行 bandit 扫描:

shell
bandit myfile.py # 扫描单个文件 bandit -r mypackage/ # 递归扫描整个包(-r表示递归)

bandit 会输出扫描结果,列出发现的安全问题及严重程度。每条结果包含问题编号(如 B301 表示 blacklist 类型的问题,对应 eval() 函数)、所在文件和行号,以及问题描述。默认输出为终端文本,也可以使用 -f 选项指定输出格式(如 -f json 生成JSON报告)。

配置最佳实践:

bandit 可以通过配置文件(如 .bandit.yaml 或在 pyproject.toml[tool.bandit] 部分)进行规则调整和忽略设置。以下是一个示例配置(这里以独立的 .bandit.yaml 为例,也可转换为 toml 格式):

yaml
targets: ['.'] # 需要扫描的目标目录(当前目录) exclude: # 需要排除的目录列表 - 'tests/' # 排除测试目录 - 'venv/' # 排除虚拟环境目录 skips: # 需要跳过的测试(规则)列表 - 'B101' # 跳过对assert语句的检查(B101) - 'B306' # 跳过对mako模板的检查(B306) - 'B308' # 跳过对HTTPSConnection的不安全验证检查(B308) - 'B309' # 跳过对SSL/TLS证书验证的检查(B309) - 'B601' # 跳过对subprocess调用的检查(B601) - 'B701' # 跳过对Jinja2自动转义的检查(B701) level: 'medium' # 报告问题的最低严重级别(可选:low, medium, high) confidence: 'medium' # 报告问题的最低置信度(可选:low, medium, high)

配置说明:

  • targets:指定要扫描的文件或目录。默认可以设为当前目录 '.',表示扫描整个项目。
  • exclude:列出需要排除的目录路径。例如,通常会排除 tests/(测试代码)、venv/.venv/(虚拟环境)以及构建输出目录等,这些地方的代码一般不是主业务逻辑,且测试中可能故意使用一些“不安全”函数(如模拟)。
  • skips:列出要跳过的 bandit 测试编号。bandit 的规则有很多,有些可能不适用项目场景。例如 B101 检查 assert 语句,在生产代码中 assert 可能被优化掉,确实不应用于安全检查,所以可以跳过。B306 针对 mako 模板的安全,若项目不使用 mako 模板可跳过。B308B309 涉及 SSL/TLS 连接的证书验证,如果项目中某些连接需要临时忽略证书验证(如内部测试环境),可以暂时跳过这些规则,但在生产环境应谨慎处理。B601 检查 subprocess 模块的使用,认为 subprocess.call 等可能有风险,如果项目广泛使用 subprocess 且已确保参数安全,可跳过此规则避免误报。B701 检查 Jinja2 模板是否开启自动转义,如果项目已统一开启自动转义,也可跳过。通过合理跳过不相关或误报较多的规则,可以减少干扰,让 bandit 报告更聚焦于真正的问题。
  • levelconfidence:这两个参数控制报告的阈值。level 指定只报告严重级别不低于该值的问题(例如设为 medium 则忽略 low 级别的问题);confidence 指定只报告置信度不低于该值的问题。bandit 对每个问题会评估严重程度(如 LOW, MEDIUM, HIGH)和置信度(表示判断该问题的确定程度)。根据项目安全要求,可以调整这些阈值。如果希望看到所有潜在问题,可设为 low;如果希望只关注高置信度的严重问题,可设为 high

上述配置示例提供了一个平衡:排除了无关目录和部分误报规则,只报告中等及以上严重程度的问题。在实际项目中,应根据自身情况调整 skipslevel 等参数。bandit 还支持在代码中通过注释忽略特定行的问题(如 # nosec B306),这在某些特殊情况下需要绕过检查时使用。

Pre-commit 集成方式:

.pre-commit-config.yaml 中添加 bandit 钩子:

yaml
- repo: https://github.com/PyCQA/bandit rev: 1.7.4 # 使用稳定版本 hooks: - id: bandit args: [-r, .] # -r表示递归扫描,.表示当前目录 exclude: ^(tests/|venv/) # 正则排除目录(与配置文件中的exclude作用类似)

这里假设项目根目录运行扫描。exclude 参数可以正则匹配要跳过的文件路径,与配置文件中的 exclude 列表作用相同。通过配置 bandit 钩子,每次提交前都会扫描代码的安全问题。如果发现高优先级的漏洞,提交将被阻止,从而促使开发者修复或确认这些问题。

徽章:

bandit 提供项目徽章,可在 README 中展示项目使用 bandit 进行安全检查:

security: bandit

markdown
[![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)

将其添加到 README 以表明代码经过 bandit 安全扫描。徽章颜色通常为黄色,提醒关注安全性。

冲突解决:

bandit 作为安全检查工具,一般不会与其他代码工具产生直接冲突。但需要注意以下几点:

  • 误报处理: bandit 可能会报告一些在特定场景下并非真正漏洞的“问题”(误报)。例如,某些 subprocess 调用或 eval 使用可能在项目中是合理且安全的,但 bandit 仍会标记。这时可以通过配置跳过相应规则(如上述 skips 列表)或在代码行添加 # nosec 注释忽略。需要在安全和开发效率之间取得平衡:既不遗漏真正的漏洞,也避免被大量误报干扰。
  • 与其他检查器的重叠: bandit 检查的问题有些可能也被其他工具部分覆盖。例如,mypy 可以捕获一些类型错误,而 bandit 捕获安全错误,两者侧重点不同。Flake8 等风格检查器不会涉及安全问题。因此,bandit 与它们是互补关系,不存在冲突。在 pre-commit 中,bandit 通常作为最后一道检查,以确保提交的代码没有明显的安全隐患。
  • 性能影响: bandit 对大型代码库的扫描可能需要一定时间。在 pre-commit 钩子中,如果代码改动不大,bandit 默认只会扫描修改的文件(除非配置了扫描整个项目)。如果感觉 bandit 影响了提交速度,可以考虑仅在 CI 中运行完整扫描,而在本地 pre-commit 中跳过 bandit 或只检查关键文件。但从安全角度出发,建议尽量在提交时进行基本的安全检查。

总体而言,bandit 可以很容易地融入现有工作流中,与其他工具并行不悖。通过合理配置,它能有效提升代码的安全性,而不会对开发流程造成重大阻碍。

autoflake

autoflake 是一个用于清理 Python 代码的小工具,主要功能是自动移除未使用的导入和未使用的变量官方仓库。在开发过程中,我们经常会导入一些模块或定义一些变量,后来不再使用但忘记删除,这会让代码显得冗余和杂乱。autoflake 可以检测并删除这些无用代码,使代码更简洁易读。此外,autoflake 默认还会移除多余的 pass 语句。

核心作用:

autoflake 的核心功能是代码清理。它充当代码“清洁工”,帮助开发者移除不再需要的代码元素。这包括:

  • 未使用的导入: 自动删除那些导入但从未在代码中使用的模块、类、函数等。这有助于减少代码冗余,避免导入过时的模块导致潜在冲突。
  • 未使用的变量: 删除函数或作用域内声明但从未使用的变量。未使用的变量往往是代码修改后的残留物,autoflake 可以将其安全地移除。
  • 多余的 pass 语句: 在空函数或类中,如果 pass 是唯一的语句且可以省略(Python 允许空块用缩进表示),autoflake 会将其删除。

通过使用 autoflake,开发者可以保持代码库的整洁,减少因冗余代码导致的阅读干扰和潜在错误。尤其是在大型项目中,定期运行 autoflake 可以清理掉那些“幽灵”导入和变量,提高代码质量。

效果对比

清理前 (autoflake)

import os import sys # 未使用 def main(): x = 1 # 未使用 print("Hello")

清理后 (autoflake)

import os def main(): print("Hello")

手动使用方式: 安装 autoflake:

shell
pip install autoflake

运行 autoflake 清理指定文件或目录。例如,清理单个文件并原地修改:

shell
autoflake --in-place --remove-unused-imports --remove-unused-variables myfile.py

常用参数说明:

  • --in-place(或 -i):直接在原文件上修改。
  • --remove-unused-imports:移除未使用的导入(默认仅对标准库模块有效,如需对所有模块生效需配合 --remove-all-unused-imports
  • --remove-unused-variables:移除未使用的变量(默认不启用,因为可能不安全。
  • --remove-duplicate-keys:移除字典字面量中重复的键(保留最后一个出现的)。
  • --expand-star-imports:展开 from module import * 为显式导入(需配合 --imports 指定模块)。
  • --check:检查哪些代码会被删除,但不实际修改文件。

例如,要递归清理整个项目并备份原文件:

shell
autoflake -i -r --remove-unused-imports --remove-unused-variables --backup .

--backup 会为修改的文件创建 .bak 备份)

配置最佳实践:

autoflake 支持通过配置文件(如 .autoflakepyproject.toml[tool.autoflake] 部分)设置默认参数。以下是一个 pyproject.toml 配置示例:

toml
[tool.autoflake] in_place = true # 默认启用原地修改 remove_unused_imports = true # 默认移除未使用的导入 remove_unused_variables = true # 默认移除未使用的变量 remove_duplicate_keys = true # 默认移除字典重复键 remove_ranges = false # 不启用移除指定范围代码的功能(除非特殊需要) check = false # 默认不启用检查模式(直接修改) include_star_imports = false # 不自动展开*导入(除非明确指定) imports = [] # 未指定要展开*导入的模块列表 exclude = [ # 需要排除的文件/目录模式 'tests/*.py', # 排除测试文件 'setup.py', # 排除安装脚本 ]

配置说明:

  • in_place = true:设置为 true 表示 autoflake 默认会直接修改文件。如果设为 false,则需要通过命令行 -i 参数才能修改文件。
  • remove_unused_imports = true:启用移除未使用导入的功能。注意默认情况下,autoflake 仅移除标准库中未使用的导入,第三方库的未使用导入不会被移除(出于安全考虑,因为第三方模块可能有副作用)。如果需要移除所有未使用导入(包括第三方),可以添加 remove_all_unused_imports = true 配置项(或使用命令行 --remove-all-unused-imports)。
  • remove_unused_variables = true:启用移除未使用变量的功能。默认不启用,因为自动删除变量存在一定风险(例如某些框架可能通过反射使用变量)。启用后,autoflake 会删除函数参数和局部变量中未被使用的声明。使用时应谨慎,确保项目中没有通过字符串或反射引用这些变量的情况。
  • remove_duplicate_keys = true:启用移除字典字面量中重复键的功能。这将保留最后出现的键值对,删除之前的重复项。
  • remove_ranges = false:此选项用于指定要移除的代码行范围(例如 --remove-ranges=5-10 删除5-10行)。通常不需要在配置中启用,保持 false 即可。
  • check = false:设为 true 时,autoflake 不会修改文件,而是输出哪些导入/变量会被删除。这里配置为 false 表示默认直接执行修改。
  • include_star_imports = false:设为 true 时,如果配合 --expand-star-imports,autoflake 会尝试展开 from module import * 为显式导入。但自动展开可能不完整,需要 --imports 指定模块。通常不建议自动展开星号导入,除非明确需要。
  • imports = []:如果启用了展开星号导入,这里列出要处理的模块名。例如 imports = ["os", "sys"] 表示遇到 from os import *from sys import * 时尝试展开。
  • exclude = [...]}:列出需要排除的文件或目录模式。例如测试文件(tests/*.py)通常包含未使用的导入(如测试框架导入但某些测试未用到),可以排除以避免干扰。setup.py 等脚本也可能不需要清理。通过排除这些文件,可以防止 autoflake 误删测试辅助代码或打包脚本中的内容。

配置好后,运行 autoflake myfile.py 就会按上述选项执行,无需每次在命令行重复参数。对于大型项目,建议将 autoflake 集成到 pre-commit 或 CI 中定期运行,以保持代码整洁。

Pre-commit 集成方式:

.pre-commit-config.yaml 中添加 autoflake 钩子:

yaml
- repo: https://github.com/PyCQA/autoflake rev: 2.1.1 # 使用稳定版本 hooks: - id: autoflake args: [ --in-place, --remove-unused-imports, --remove-unused-variables, --remove-duplicate-keys, ] exclude: ^(tests/|setup\.py)$ # 正则排除测试目录和setup.py

这样每次提交前,autoflake 都会自动清理未使用的导入和变量。如果有代码被修改,autoflake 会更新文件并阻止提交,提示重新提交修改后的内容。

徽章:

autoflake 没有官方徽章,但可以在 README 中提及项目使用 autoflake 清理冗余代码

冲突解决:

autoflake 通常不会与其他工具产生冲突,因为它处理的是代码中的冗余部分,而其他工具(格式化、检查)处理的是风格或正确性问题。需要注意的是:

  • 与 isort 的顺序: 如果同时使用 isort 和 autoflake,建议先运行 autoflake 再运行 isort。因为 autoflake 可能会删除一些导入,之后 isort 可以重新排序剩余的导入,确保最终导入列表既干净又有序。如果顺序相反,isort 排序后 autoflake 又删除导入,可能导致不必要的格式调整。
  • 与类型检查的交互: autoflake 可能会删除一些看似未使用但实际上被类型检查需要的导入。例如,在 Python 3.9 之前,List, Dict 等类型需要从 typing 导入,如果代码中没有显式使用这些类型(仅在类型注解中使用),某些工具可能认为它们未被使用。autoflake 默认不会删除 typing 模块的导入,因为这些导入通常用于类型提示(autoflake 对 typing 模块有特殊处理,避免删除必要的类型导入。不过,如果使用了 from __future__ import annotations 或 Python 3.10+ 的标准类型,可能可以安全地移除一些 typing 导入。在这种情况下,autoflake 会正确删除不再需要的导入。因此,只要配置得当,autoflake 不会破坏类型检查。但为安全起见,建议在运行 autoflake 后检查一下类型注解是否仍然有效,特别是在使用 --remove-unused-variables 时,确保没有删除类型注解中引用的变量。
  • 误删风险: autoflake 自动删除代码存在一定风险,例如某些框架通过字符串引用变量名(如 Django ORM 的字段名),autoflake 无法识别这种间接引用,可能会将相关变量删除导致错误。因此,在首次使用 autoflake 时,应仔细检查其修改,确保没有误删重要代码。对于关键项目,建议在 CI 中运行 autoflake 的检查模式(--check),将其作为警告而非强制错误,以人工确认后再自动清理。

总的来说,autoflake 是一个非常有用的辅助工具,与其他代码质量工具配合可以进一步提升代码库的整洁度。通过合理配置和注意上述事项,可以安全地将其纳入开发流程,享受自动清理冗余代码的便利。

pyupgrade

pyupgrade 是一个用于自动升级 Python 代码语法的工具。它可以扫描代码并将旧版本 Python 的语法升级为更新版本支持的更简洁或更安全的形式官方仓库。例如,pyupgrade 会将 Python 2 兼容的语法转换为 Python 3 风格,或者将较老的 Python 3.x 语法升级为更新版本的语法。通过使用 pyupgrade,开发者可以快速让代码库符合更高版本 Python 的最佳实践,减少维护旧语法的负担。

核心作用: pyupgrade 的核心功能是代码语法现代化。它能够自动执行许多常见的语法升级操作,包括:

  • 删除 Python 2 兼容代码: 移除 print 语句的括号缺失形式、旧式的八进制字面量(0o 前缀替代 0 前缀)、dict.iteritems() 等 Python 2 特有方法,以及 from __future__ 中不再需要的导入(如 unicode_literals)等。
  • 更新为新语法:dict() 构造器包裹的列表推导式改为字典推导式,将 set() 构造器包裹的列表推导式改为集合推导式,将旧式的格式化字符串 %s 替换为 str.format() 或 f-string(根据版本)等。
  • 升级类型注解: 在较新的 Python 版本中,某些类型注解语法得到简化(如 typing.List[int] 可简化为 list[int])。pyupgrade 可以根据目标版本自动更新这些注解。
  • 其他改进: 例如,将 except Exception, e 旧语法改为 except Exception as e,将 xrange 替换为 range(针对仍存在的 Python 2 兼容代码),移除不必要的括号等。

通过这些转换,pyupgrade 帮助开发者无痛升级代码库以利用新语言特性,同时减少人工逐一修改的工作量。对于维护多个 Python 版本支持的项目,pyupgrade 也可以根据指定的最低版本自动调整代码,确保代码只包含目标版本支持的语法。

效果对比

升级前 (pyupgrade)

# Python 2/3 兼容代码 my_set = set([1, 2, 3]) my_string = "Value is {}".format(42)

升级后 (pyupgrade --py38-plus)

# 现代 Python 3.8+ 代码 my_set = {1, 2, 3} my_string = f"Value is {42}"

手动使用方式: 安装 pyupgrade:

shell
pip install pyupgrade

运行 pyupgrade 升级指定文件或目录。例如:

shell
pyupgrade --py310-plus myfile.py # 将文件升级为兼容Python 3.10+的语法 pyupgrade --py39-plus mypackage/ # 递归升级整个包为Python 3.9+兼容

常用参数:

  • --py310-plus--py39-plus 等:指定目标支持的最低 Python 版本。pyupgrade 将根据该版本移除不再需要的兼容代码并应用新语法。例如 --py310-plus 会启用诸如使用 match/case 语法(如果存在旧的 if/elif 链可以转换)、将 isinstance(x, (A, B)) 替换为 isinstance(x, A | B)(联合类型语法)等升级。
  • --keep-percent-format:保留 % 格式化字符串,不将其升级为 .format() 或 f-string。
  • --keep-mock:保留 mock 模块的导入(否则 pyupgrade 会将 import mock 替换为 from unittest import mock)。
  • --exit-zero-even-if-changed:即使有代码被修改,也返回退出码0(默认情况下,pyupgrade 修改了文件会返回1)。
  • --check:检查哪些代码会被升级,但不实际修改文件。

pyupgrade 的默认行为是直接修改文件。建议在运行前备份代码或通过版本控制管理,以便查看改动。

配置最佳实践: pyupgrade 可以通过命令行参数或配置文件(如 pyproject.toml[tool.pyupgrade] 部分)指定常用选项。以下是一个配置示例:

toml
[tool.pyupgrade] minimum_python_version = [3, 10] # 指定最低支持版本为Python 3.10 keep_percent_format = false # 不保留%格式化,尽量升级为更现代的格式 keep_mock_imports = false # 允许将mock导入替换为from unittest import mock exit_zero = false # 修改文件时返回非零状态(用于CI检测)

配置说明:

  • minimum_python_version = [3, 10]:这是 pyupgrade 最重要的配置项,决定了升级的目标版本。它等同于命令行的 --py310-plus。设置后,pyupgrade 将应用所有适用于 Python 3.10+ 的升级规则。例如,移除 print 语句(Python 3.10 不再支持)、使用 list/dict 等泛型类型简写、将旧的异常语法升级等。根据项目实际支持的最低版本,可设置为 [3, 8][3, 9] 等。
  • keep_percent_format = false:pyupgrade 默认会尝试将 % 格式化字符串升级为 .format() 或 f-string。如果项目中希望保留某些 % 格式化(例如出于性能或习惯原因),可以设置此项为 true。否则保持 false,让 pyupgrade 自动升级以提高代码可读性。
  • keep_mock_imports = false:pyupgrade 默认会将 import mock 替换为 from unittest import mock,因为标准库在 Python 3.3+ 已包含 unittest.mock。如果项目仍在使用第三方 mock 库(例如兼容 Python 2),可设为 true 保留原导入。否则设为 false 以符合标准做法。
  • exit_zero = false:pyupgrade 在修改了文件时默认返回退出码1。这有助于在 CI 中检测到代码需要升级。如果希望即使有修改也返回0(不视为错误),可设为 true。但通常建议保持 false,以便 CI 发现代码未升级的情况,提醒开发者提交升级后的代码。

通过上述配置,pyupgrade 将按照目标版本自动升级代码。在大型项目中,建议先在 CI 中以检查模式运行 pyupgrade,确认所有修改都是预期的,然后再允许其自动修改文件。此外,pyupgrade 不会处理代码逻辑,仅语法层面的升级,因此升级后应运行测试确保没有引入意外问题。

Pre-commit 集成方式:.pre-commit-config.yaml 中添加 pyupgrade 钩子:

yaml
- repo: https://github.com/asottile/pyupgrade rev: v3.15.0 # 使用稳定版本 hooks: - id: pyupgrade args: [--py310-plus] # 指定升级目标版本

这样每次提交前,pyupgrade 都会检查代码并升级语法。如果有代码被修改,pyupgrade 会更新文件并阻止提交,提示重新提交升级后的内容。需要注意的是,pyupgrade 应在代码格式化工具之前运行,因为它可能改变代码结构,随后再由 Black 等格式化可以保证风格一致。例如,在 pre-commit 配置中,pyupgrade 钩子应排在 Black 钩子前面[github.com]

徽章: pyupgrade 没有官方徽章,但可以在 README 中说明项目使用 pyupgrade 来保持代码符合最新 Python 语法。如果需要,也可以使用 shields.io 生成自定义徽章。

冲突解决: pyupgrade 主要涉及语法层面的修改,与其他工具的交互需要注意以下几点:

  • 与代码格式化工具的顺序: 正如上面提到的,pyupgrade 应在 Black 等格式化工具之前运行。因为 pyupgrade 可能会改变代码的结构(如添加或删除括号、换行等),如果先运行 Black 再运行 pyupgrade,可能导致 Black 已经格式化好的代码被 pyupgrade 修改,从而需要再次运行 Black。因此,在 pre-commit 钩子顺序上,将 pyupgrade 放在前面,Black 放在后面,可以避免这种反复。

  • 与类型检查器的配合: pyupgrade 升级类型注解(如将 List[int] 改为 list[int])后,mypy 等类型检查器在对应 Python 版本下能够正确识别这些新语法。但需要确保 mypy 配置的 target-version 与 pyupgrade 的目标版本一致,否则 mypy 可能不认识新语法(例如在 Python 3.8 项目中使用 list[int] 会报错)。因此,升级代码后应同步更新 mypy 等工具的版本配置,以利用新特性并避免误报。

  • 避免过度升级: pyupgrade 非常强大,但也可能“升级”一些本不想改动的地方。例如,它会把所有 %s 格式化升级,包括那些可能出于性能考虑而有意保留的。为了避免此类问题,可以使用配置选项(如 keep_percent_format)精细控制,或在特定代码行前添加注释 # pyupgrade: ignore 来阻止 pyupgrade 修改该行。例如:

    python
    logger.debug("Performance critical: %s", data) # pyupgrade: ignore

    这样 pyupgrade 就不会将这行的 %s 格式化升级。合理使用这些忽略机制,可以在享受自动升级便利的同时保留个别需要的旧语法。

  • 与测试和人工审查: 自动升级后,务必运行项目测试以确保升级没有破坏功能。某些语法升级(如 isinstance(x, (A, B)) 改为 isinstance(x, A | B))在 Python 3.10+ 是等价的,但在旧版本不兼容,所以只要配置了正确的最低版本,这不会有问题。但如果有疏漏,可能引入错误。因此,pyupgrade 应结合测试和人工审查,作为升级流程的一环,而非完全放任。

总的来说,pyupgrade 是一个非常有用的工具,能够大幅减少升级 Python 版本或清理旧语法的工作量。通过正确配置和与其他工具的配合,可以安全地将其融入开发流程,让代码库始终保持在较新且简洁的语法形式。

pydocstringformatter

pydocstringformatter 是一个用于**自动格式化 Python 文档字符串(docstring)**的工具。它可以检查并重新格式化 docstring,使其符合 PEP 257 等文档字符串风格指南的要求官方仓库。例如,pydocstringformatter 会在多行 docstring 的结尾添加一个空行,统一字符串引号风格,调整缩进,以及移除多余的空白等。通过使用该工具,开发者可以确保项目中的 docstring 格式一致、整洁易读。

核心作用:

pydocstringformatter 的核心功能是规范文档字符串格式。它自动执行以下改进:

  • 统一引号风格: 将 docstring 的引号统一为单引号或双引号(可配置),并确保结束引号独占一行(对于多行 docstring)。
  • 添加/移除空行: 根据 PEP 257,多行 docstring 应该在结尾处有一个空行与代码分隔。pydocstringformatter 会自动添加缺失的空行,或删除多余的空行,使 docstring 结构符合规范。
  • 调整缩进: 对于类或函数内部的 docstring,确保其缩进与所在作用域一致,避免多余的缩进空格。
  • 清理空白: 移除 docstring 内部行首或行尾的多余空格,纠正换行符,使文档内容更清晰。
  • 支持多种风格: pydocstringformatter 可以配置以遵循不同的文档字符串风格(如 Google 风格、NumPy 风格、reStructuredText 风格等),确保格式化后的 docstring 在参数描述、返回值说明等部分的格式符合所选风格的要求[peps.python.org][sphinxcontrib-napoleon.readthedocs.io]

通过自动执行这些格式化操作,pydocstringformatter 帮助开发者维护一致的文档风格,减少因为格式不一致而产生的代码审查噪音。同时,格式良好的 docstring 也能被 Sphinx 等文档生成工具更好地解析,生成美观的 API 文档。

手动使用方式:

安装 pydocstringformatter:

shell
pip install pydocstringformatter

运行 pydocstringformatter 格式化指定文件或目录的 docstring:

shell
pydocstringformatter --write {source_file_or_directory}

配置最佳实践:

pydocstringformatter 支持通过配置文件(如 pyproject.toml[tool.pydocstringformatter] 部分)设置选项。以下是一个配置示例:

[tool.pydocstringformatter] # 设置为 true,将直接修改文件。如果为 false(默认),则仅打印差异(diff)。 # 在 pre-commit 中通常需要设置为 true。 write = true # 排除不需要格式化的文件或目录(glob 模式) # exclude = ["**/my_dir/**", "**/my_other_dir/**"] strip-whitespaces = true split-summary-body = true # NumPy 风格章节标题下划线长度,false 为不限制 numpydoc-section-hyphen-length = false # 可选:指定要遵循的风格指南。例如,如果项目同时使用 numpydoc 风格,可以添加它。 # 默认为 ["pep257"]。 # style = ["pep257", "numpydoc"]

Pre-commit 集成方式:

.pre-commit-config.yaml 中添加 pydocstringformatter 钩子:

# .pre-commit-config.yaml - repo: https://github.com/DanielNoord/pydocstringformatter rev: v0.7.3 hooks: - id: pydocstringformatter # 在 pre-commit 中,通常通过 args 来传递 --write 标志 args: ["--write"]

这样每次提交前,pydocstringformatter 都会检查并格式化 docstring。如果有修改,会更新文件并阻止提交,提示重新提交格式化后的内容。

徽章:

pydocstringformatter 没有官方徽章,但可以在 README 中说明项目使用该工具维护 docstring 格式。

冲突解决:

pydocstringformatter 主要关注文档字符串的格式,与其他代码工具的冲突较少。需要注意的是:

  • 与 Black 的配合: Black 格式化器也会对 docstring 进行一些调整,例如统一引号为单引号,调整换行等。pydocstringformatter 和 Black 在这些方面可能会有重复工作。但通常两者是互补的:Black 处理整体代码格式,pydocstringformatter 专注于 docstring 内部的细节(如结尾空行、缩进等)。为了避免冲突,建议在 pre-commit 中先运行 pydocstringformatter 再运行 Black。因为 pydocstringformatter 可能会在 docstring 末尾添加空行或调整缩进,Black 随后可以确保这些改动符合整体代码风格(如删除多余空行,如果不符合其规则)。一般情况下,两者都会使 docstring 更规范,不会产生矛盾。
  • 与 pydocstyle 检查的关系: pydocstringformatter 本身基于 pydocstyle 的规则来修复问题。如果项目另外使用 pydocstyle 或 flake8 的 D 类错误检查 docstring 风格,那么启用 pydocstringformatter 后,大部分格式问题会被自动修复,从而减少检查时的报错。但需要注意配置的一致性,例如忽略的错误码应在检查工具中也忽略,否则格式化后仍可能有警告。
  • 保留特殊格式: 某些 docstring 可能包含特殊格式,例如嵌入的 reST 标记、代码块或表格。pydocstringformatter 在默认情况下不会格式化包含代码块的 docstring(除非使用 --force)。如果希望保留 docstring 中的特定结构(如示例代码),可以在配置中排除这些 docstring 或者使用 --force 时谨慎操作。此外,pydocstringformatter 不会修改 docstring 内部的内容语义,只会调整格式,因此无需担心其破坏文档内容。

总的来说,pydocstringformatter 是维护 Python 项目文档一致性的有力助手。它与代码格式化和检查工具配合,可以确保代码不仅功能正确、风格统一,连文档都整整齐齐。通过合理配置和集成,开发者可以从繁琐的 docstring 格式调整中解放出来,专注于编写高质量的文档内容。

ruff

ruff 是近年来新兴的一款高速 Python 代码分析工具,它将多种传统工具的功能集于一身官方文档。Ruff 最初作为 Flake8 的替代者出现,但现已发展为一个全能型工具,内置了**代码检查(linter)代码格式化(formatter)**两大功能。它支持超过 1000 条代码规则,几乎涵盖了 Flake8 及其众多插件的检查能力,同时还内置了类似 isort 的导入排序、类似 pydocstyle 的文档字符串检查、类似 pyupgrade 的语法升级等功能。更重要的是,Ruff 由 Rust 编写,运行速度极快,通常比传统 Python 工具快 10-100 倍。

核心作用:

Ruff 的核心作用可以概括为一站式代码质量保障。它通过一个工具提供了以下主要功能:

  • 代码检查(Linting): Ruff 内置了大量代码规则,包括 PEP 8 风格检查、常见错误检测、最佳实践检查等。例如,它可以发现未使用的变量、错误的缩进、不规范的命名、潜在的逻辑错误等,功能相当于 Flake8 加上众多插件(如 flake8-bugbear、flake8-comprehensions 等)的总和。Ruff 还支持错误自动修复(fix),对于许多问题可以一键修正,提高开发效率。
  • 代码格式化: Ruff 附带了一个快速的代码格式化器,其目标是作为 Black 的直接替代品。Ruff formatter 会按照类似 Black 的风格重新格式化代码,包括行长度限制、括号换行、空格插入等,使代码风格统一。对于已经使用 Black 的项目,Ruff formatter 在大多数情况下可以产生几乎相同的格式化结果。
  • 导入排序: Ruff 实现了与 isort 相当的导入排序功能,能够自动将导入语句按标准库、第三方库、本地库等分类排序,并应用正确的格式。在 Ruff 中,这表现为一系列导入相关的规则(如 I001 未排序的导入),并可以自动修复。因此,使用 Ruff 后,通常不再需要单独的 isort 工具。
  • 文档字符串检查: Ruff 包含了 pydocstyle 的规则(错误码以 D 开头),可以检查 docstring 是否符合规范,如是否存在、格式是否正确等。开发者可以选择遵循的文档风格(Google、NumPy 等)并在 Ruff 中配置。
  • 语法升级和清理: Ruff 还实现了类似 pyupgrade 和 autoflake 的功能。例如,它可以自动将旧语法升级(如将 x == None 改为 x is None,将 dict.iteritems() 改为 dict.items() 等),以及移除未使用的导入和变量。这些改进很多可以通过 Ruff 的自动修复完成,从而减少人工操作。

通过上述丰富的功能,Ruff 旨在成为单一的代码质量工具,替代过去需要多个工具组合才能完成的任务[docs.astral.sh]。这不仅简化了项目配置,也显著提升了运行速度,使开发者可以更频繁地运行检查,从而在早期发现问题。

手动使用方式:

安装 Ruff:

shell
pip install ruff

使用 Ruff 进行代码检查(lint):

shell
# 运行 linter ruff check . # 自动修复可修复的问题 ruff check . --fix # 运行 formatter ruff format .

--fix 参数非常有用,Ruff 会对可修复的问题直接修改文件(类似 black 的行为)。对于检查结果,可以使用 --format 指定输出格式,或使用 --diff 查看修复差异。

使用 Ruff 进行代码格式化:

shell
ruff format myfile.py # 格式化单个文件 ruff format mypackage/ # 递归格式化整个包

Ruff formatter 会直接修改文件以应用格式更改。可以使用 --check 仅检查不修改,或 --diff 查看格式化差异。

Ruff 还提供了一些便捷的命令,例如 ruff upgrade 可以交互式升级项目的 Ruff 版本和配置,ruff rules 可以列出所有可用规则及其说明等。

配置最佳实践: Ruff 的配置通常放在 pyproject.toml[tool.ruff] 部分(或单独的 .ruff.toml 文件)。由于 Ruff 功能广泛,配置项也较多,但可以根据需要逐步调整。以下是一个推荐的配置示例,涵盖关键选项并附带注释:

toml
[tool.ruff] # 基本配置 line-length = 88 # 代码行最大长度(与Black/Ruff formatter一致) target-version = "py310" # 目标Python版本,影响可用规则和语法检查 select = [ # 需要启用的规则类别/编号 "A", # 防止关键字覆盖内置名称 (Avoid shadowing builtins) "B", # bugbear 规则,额外的错误和最佳实践检查 "C", # 逗号风格检查 (Commas) "D", # pydocstyle 文档字符串规则 "E", # pycodestyle (PEP8) 错误 "F", # pyflakes 错误(未使用变量、导入等) "I", # isort 导入排序规则 "N", # pep8-naming 命名规范 "Q", # flake8-quotes 引号风格 "T20", #eradicate 规则,检测调试打印语句 "W", # pycodestyle (PEP8) 警告 ] ignore = [ # 需要忽略的具体规则或错误码 "D100", # 忽略:公共模块缺少文档字符串 "D101", # 忽略:公共类缺少文档字符串 "D102", # 忽略:公共方法缺少文档字符串 "D103", # 忽略:公共函数缺少文档字符串 "D104", # 忽略:公共包缺少文档字符串 "D105", # 忽略:魔术方法缺少文档字符串 "D107", # 忽略:类的 __init__ 缺少文档字符串 "E501", # 忽略:行过长(由 formatter 处理) "W503", # 忽略:行拼接运算符前换行(与 formatter 规则冲突) ] extend-ignore = [ # 从其他配置继承忽略的规则(如果有) # "E203", # 例如:忽略冒号前空格(PEP8 E203,Black允许) ] exclude = [ # 需要排除检查/格式化的文件或目录 "tests/", # 测试目录 "venv/", # 虚拟环境 "build/", # 构建输出 "__pycache__", # Python 缓存目录 ] # Linter 特定配置 [tool.ruff.lint] fixable = "all" # 允许自动修复所有可修复的问题 unfixable = [] # 明确指定不可自动修复的问题(空表示无) cache = true # 启用检查缓存以加速后续运行 show-error-codes = true # 输出中显示错误代码 force-exclude = true # 严格排除匹配的文件(即使父目录未被排除) # 可以针对特定规则设置参数,例如: # [tool.ruff.lint.pydocstyle] # convention = "google" # 配置 pydocstyle 使用 Google 风格 # Formatter 特定配置 [tool.ruff.format] quote-style = "single" # 字符串使用单引号(类似 Black) indent-width = 4 # 缩进宽度4空格 line-length = 88 # 格式换行长度(应与上面的 line-length 一致) # 其他格式选项如: # trailing-comma = "es5" # 类似 Black,对容器字面量添加尾逗号 # wrap-comments = true # 自动换行注释

配置说明:

  • line-length = 88:设置代码行最大长度。Ruff 的检查器和格式化器都会遵守此值。建议与 Black 等保持一致,以避免冲突。
  • target-version = "py310":指定项目目标支持的最低 Python 版本。这会影响 Ruff 可以应用的规则,例如某些语法检查只在特定版本下有意义。Ruff 会根据此值启用或禁用相应规则。
  • select = [...]}:列出需要启用的规则类别或具体错误码。Ruff 提供了许多规则分类,如 "A" 代表避免内置名称冲突的规则,"B" 代表 bugbear 规则,"D" 代表文档字符串规则等。通过选择这些类别,可以覆盖广泛的检查范围。如果希望精简检查,可以只选择部分类别。也可以直接指定规则编号(如 "F401" 表示未使用的导入)。
  • ignore = [...]}:列出要忽略的具体错误码。例如,D100-D105, D107 是文档字符串缺失的错误,如果项目不强制每个公共对象都有 docstring,可以暂时忽略这些。E501 是行过长错误,由于我们使用 formatter 来处理行宽,因此忽略此检查。W503 是关于运算符位置的警告,与 formatter 的风格冲突,也予以忽略。通过 ignore 可以过滤掉不关心或与 formatter 重复的问题。
  • extend-ignore = [...]}:如果从其他配置文件继承了忽略列表,可以用此扩展,而不会覆盖原有忽略项。
  • exclude = [...]}:列出要排除的文件或目录模式。这些路径下的文件不会被 Ruff 检查或格式化。例如测试目录、虚拟环境、构建产物目录等通常不需要检查。
  • [tool.ruff.lint] 部分:配置检查器特有的选项。fixable = "all" 表示允许 Ruff 自动修复所有可修复的问题(Ruff 默认也是如此)。cache = true 启用检查缓存,大幅提升重复运行时的速度。show-error-codes = true 让输出显示错误码,方便参考文档。如果需要针对某些规则调整行为,也可以在子部分配置,例如 [tool.ruff.lint.pydocstyle] 下设置文档风格等。
  • [tool.ruff.format] 部分:配置格式化器的选项。quote-style = "single" 指定字符串使用单引号。indent-width = 4 是缩进空格数。line-length 应与顶层的 line-length 一致。Ruff formatter 还有一些高级选项,如 trailing-comma(控制尾逗号添加策略)、wrap-comments(自动换行注释)等,可以根据需要启用。

通过上述配置,Ruff 将执行严格的代码检查和一致的代码格式化。在实际使用中,可根据项目反馈调整 selectignore 列表,逐步收紧或放宽规则。Ruff 的文档提供了完整的[配置选项列表][规则列表],方便查阅每个规则的含义和用途。

Pre-commit 集成方式:

.pre-commit-config.yaml 中添加 Ruff 钩子。由于 Ruff 同时具备检查和格式化功能,通常需要两个钩子分别处理:

yaml
# .pre-commit-config.yaml - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.4.4 hooks: # 运行 linter 并自动修复 - id: ruff args: [--fix, --exit-non-zero-on-fix] # 运行 formatter - id: ruff-format

这样每次提交前,Ruff 会先检查并修复代码问题,然后格式化代码。如果有任何改动,提交将被阻止,提示重新提交修改后的内容。需要注意的是,Ruff formatter 本身已经可以处理大部分风格问题,包括导入排序和代码格式化,因此通常不再需要同时使用 isort 或 Black 等工具。如果项目之前依赖这些工具,引入 Ruff 后应移除对应的钩子,以免重复处理。

**徽章:

Ruff

[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

冲突解决:

Ruff 作为一站式工具,在减少工具数量的同时,也避免了许多传统工具之间的冲突。不过,在与其他工具配合或配置不当时,仍需注意以下几点:

  • 替代其他工具: Ruff 的目标就是替代 Flake8、Black、isort 等工具。因此,在项目中引入 Ruff 后,应移除这些旧工具的配置和钩子,否则可能出现重复检查或格式冲突。例如,如果继续使用 isort,Ruff 也会检查导入顺序,两者可能产生不一致。如果继续使用 Black,Ruff formatter 和 Black 都尝试格式化代码,可能导致竞争。因此,建议完全用 Ruff 替换相关工具,以发挥其最大效用。
  • 与 Black 等格式化器的差异: Ruff formatter 虽然目标是兼容 Black,但在某些细节上可能略有不同。例如,对注释的处理、行拆分的策略等。如果项目原先使用 Black,切换到 Ruff formatter 时,大部分代码格式不会有明显变化,但可能会有少数行的格式调整。这通常不是冲突,而是风格统一过程中的一次性差异。可以通过运行一次 ruff format 来同步格式,之后 Ruff 会保持一致。Ruff 官方也建议在迁移时允许一次格式调整提交,之后就不会有意外的改动。
  • 配置一致性: 在 Ruff 中,检查器和格式化器共享部分配置(如行宽)。需要确保 line-length 等在检查和格式化中一致,否则可能出现检查报错但格式化无法解决的情况。此外,如果项目同时使用 Ruff 和 mypy,应注意 Ruff 的某些规则可能与 mypy 有重叠(例如变量未使用,mypy 也可能报告)。但两者侧重点不同:mypy 关注类型,Ruff 关注语法和风格,不会真正冲突。
  • 规则冲突与误报: 由于 Ruff 规则众多,某些规则之间可能存在冲突或在特定情况下误报。例如,Ruff 的 W503(运算符前换行)和 W504(运算符后换行)是互斥的,配置时应只启用其中一个(通常 Ruff 会默认处理这种情况)。如果遇到误报,可以通过 ignore 忽略特定规则或使用 # noqa: CODE 注释忽略某行。Ruff 社区活跃,规则也在不断改进,遇到问题可以查阅文档或提交反馈。

总的来说,使用 Ruff 可以极大简化 Python 项目的工具链,并显著提高代码检查和格式化的效率。通过正确的配置和替换旧工具,Ruff 能够与项目现有流程无缝衔接,带来更快的反馈循环和更一致的代码质量。对于追求高效和简洁的团队来说,Ruff 是一个值得尝试的现代代码质量工具。


完整配置与最佳顺序

将所有工具整合在一起时,执行顺序至关重要。一个合理的顺序可以避免工具间的相互干扰,例如,避免 linter 报告一个即将被 formatter 修复的问题。下面提供一个经过深思熟虑的完整配置方案。

推荐的检查顺序与理由

基本原则是:先清理和升级,再格式化,最后进行质量与安全检查。

  1. 通用检查 (pre-commit-hooks): 首先运行一些与语言无关的基础检查,如修复文件末尾空行、检查大的二进制文件等。
  2. 语法升级 (pyupgrade): 在格式化之前升级语法,因为新语法可能有不同的最佳格式。
  3. 代码清理 (autoflake): 移除未使用的导入和变量,避免后续工具对这些冗余代码进行格式化或检查。
  4. 导入排序 (isort): 在主格式化工具之前整理 imports。
  5. 代码格式化 (black, pydocstringformatter): 应用统一的代码风格。
  6. 安全检查 (bandit): 在代码结构稳定后进行安全扫描。
  7. 类型检查 (mypy): 作为最耗时、最深入的检查之一,放在最后,确保在代码格式和内容都确定后进行。

如果使用 ruff,顺序会大大简化:pre-commit-hooks -> ruff (lint & format) -> bandit -> mypy

完整 .pre-commit-config.yaml 示例

以下是一个包含所有介绍工具(非 ruff 版本)的完整配置文件,并展示了如何集成一个本地脚本。

# .pre-commit-config.yaml # 参见:https://pre-commit.com/ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: - id: trailing-whitespace # 移除行尾多余的空格 - id: end-of-file-fixer # 确保文件以单个空行结尾 - id: check-yaml # 检查 YAML 文件语法 - id: check-added-large-files # 防止大文件被意外提交 - repo: https://github.com/asottile/pyupgrade rev: v3.16.0 hooks: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/PyCQA/autoflake rev: v2.3.1 hooks: - id: autoflake args: - --in-place - --remove-all-unused-imports - --remove-unused-variables - repo: https://github.com/pycqa/isort rev: 5.13.2 hooks: - id: isort name: isort (python) - repo: https://github.com/psf/black rev: 24.4.2 hooks: - id: black - repo: https://github.com/DanielNoord/pydocstringformatter rev: v0.7.3 hooks: - id: pydocstringformatter args: ["--write"] - repo: https://github.com/PyCQA/bandit rev: 1.7.9 hooks: - id: bandit args: ["-c", "pyproject.toml"] additional_dependencies: ["bandit[toml]"] - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.10.0 hooks: - id: mypy additional_dependencies: [types-requests] # 示例:如何集成一个本地脚本作为 hook - repo: local hooks: - id: custom-check name: Custom local check entry: python scripts/my_custom_check.py language: python types: [python] # 假设 scripts/my_custom_check.py 是一个自定义检查脚本

总结:构建高效的代码质量工作流

通过本文的探讨,我们构建了一个全面的 Python 代码标准化工程体系。然而,如何将这些工具最高效地融入日常开发流程,是实现其价值的最后一步。这需要区分不同工具的最佳应用场景。

工具应用阶段划分

  • 实时检查 (On-the-fly in IDE):

    这一阶段的目标是在编写代码的瞬间就获得反馈,避免错误累积。适合此阶段的工具应具备极高的响应速度和良好的编辑器集成。

    首选工具: Ruff (通过其 VSCode 扩展) 或 Pylance。它们能提供即时的错误高亮、格式化、导入排序和代码建议,将质量控制前置到编码的每一秒,极大地提升了开发效率和体验。

  • 提交前检查 (Pre-commit Stage):

    这是代码进入版本库前的最后一道质量门禁,其核心目标是“防错”而非“纠错”。此阶段的检查必须是强制性的、全面的。

    首选工具: 所有工具都应在此阶段通过 pre-commit 框架进行配置。这确保了即使开发者的 IDE 配置不当或被暂时禁用,所有代码在合入团队仓库前也必须通过统一的、严格的标准化流程。它是保障团队代码质量下限的基石。

核心建议

  1. 从小处着手,逐步推进: 对于一个全新的项目,可以直接应用本文推荐的完整配置。但对于一个庞大的存量项目,一次性引入所有工具可能会带来巨大的修复工作。建议从 blackisort 开始,首先统一代码风格,然后再逐步引入 autoflake, pyupgrade, mypybandit
  2. 拥抱 ruff,简化未来: 对于新项目或愿意进行迁移的团队,可以尝试使用 ruff。它不仅能带来显著的性能提升,更能极大简化工具链的管理和配置,是 Python 代码质量工具的未来趋势。
  3. 配置即文档,规范即共识: 将 pyproject.toml.pre-commit-config.yaml 文件视为项目的重要技术文档。清晰的配置、详尽的注释本身就是一种团队规范的沉淀和共识的体现。

本文作者:Silon汐冷

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!