☰
用 PEP 8 写出漂亮的 Python 代码:缩进、空白、命名与格式化实战
2026/10/15 3:43:44 网站建设 项目流程
  • 示例工程
  • 教程

【免费下载链接】materials

Bonus materials, exercises, and example projects for our Python tutorials

项目地址:https://gitcode.com/gh_mirrors/ma/materials
点击查看免费下载

导读

本文以仓库中 pep8-beautiful-code 目录为核心素材,系统讲解 PEP 8 规范中与日常编码最密切的几大主题:缩进与制表符、行长度与续行、尾随空白、命名约定以及文档字符串,并带你亲自动手用 Ruff 对不规范代码进行 lint 和自动格式化。读完本文,你将理解常见 PEP 8 违规的产生原因,掌握如何借助 linter 与格式化工具把代码打磨成符合社区标准、易读易维护的"漂亮代码"。

PEP 8 是什么?为什么"漂亮"的代码有规范可循

PEP 8(Python Enhancement Proposal 8)是 Python 官方提出的代码风格指南,它不改变程序的行为,却深刻影响代码的可读性、可维护性与团队协作效率。PEP 8 的核心理念是:在 Python 社区里,代码的写法比"能跑"更重要——一致的风格让不同开发者写出的代码看起来像同一个人写的,降低阅读与审查成本。

本仓库的 pep8-beautiful-code 目录正是为配套讲解 PEP 8 的实战教程而准备的一组示例文件。它用"先故意写坏,再修复"的方式,让你直观感受每一类风格问题的表现形式与修复后的效果:

  • mixed_indentation.py:演示混用空格与 Tab 缩进的问题;
  • trailing_whitespace.py:演示行尾尾随空白的问题;
  • unfashionable.py:演示格式化前的"不修边幅"代码,同时保留格式化后的对照版本;
  • quadratic.py、variance.py:提供符合规范的功能性示例,便于对比;
  • documented_module.py:演示模块级文档字符串的写法。

三个"问题文件":亲自动手复现 PEP 8 违规

1. 混合缩进:Tab 与空格之争

Python 2 中曾经允许混用 Tab 和空格缩进(只要整体缩进层级一致),但 Python 3 直接报TabError: inconsistent use of tabs and spaces in indentation,代码根本无法运行。即使不报错,混用缩进也会让代码在不同编辑器下呈现截然不同的对齐效果。

仓库中的 mixed_indentation.py 用注释形式保存了这段"反面教材":

# def mixed_indentation(greet=True): # if greet: # print("Hello") # Indented with 4 spaces. # print("World") # Indented with a tab. # # mixed_indentation()

注意第 7 行缩进用的是 Tab,而第 6 行用的是 4 个空格。按照文件开头的说明,把注释取消后这段代码会触发TabError。修复方法很明确:

  • 整个项目统一使用4 个空格作为一级缩进;
  • 在编辑器中开启"显示不可见字符",避免 Tab 悄悄混入;
  • 配置编辑器自动把 Tab 展开为空格。

2. 尾随空白:看不见的垃圾字符

行尾多余的空格或 Tab 属于"看不见的脏东西":它不影响运行,却会污染 diff、干扰某些工具的输出,PEP 8 明确禁止。仓库中的 trailing_whitespace.py 展示了典型场景——反斜杠续行后紧跟空格:

# x = 1 + 2 + \ # w291 # 3 + 4 # # print(x)

这里\之后、注释# w291之前有空格。w291正是 pycodestyle 为"尾随空白"(trailing whitespace)分配的警告码,教程中演示的 linter 正是借助这类规则码定位问题。修复方法同样简单:保存文件前删除行尾空白,或交给格式化工具自动清理。

3. 格式化前的"不修边幅"代码

unfashionable.py 是本次实战的主角。文件顶部是经过 lint 与格式化之后的整洁版本,底部注释块则是修复前的原始代码:

numbers = [1, 2, 3, 4] def add_all_numbers_from_collection( number_one, number_two, number_three, number_four ): return number_one + number_two + number_three + number_four print(add_all_numbers_from_collection(*numbers))

对照被注释的"原始版",可以看到它同时犯了好几类风格错误:

# import math # numbers = [1,2,\ # 3,4] # def add_all_numbers_from_collection( # number_one, number_two, number_three, # number_four): # return number_one+number_two + number_three +number_four # print (add_all_numbers_from_collection( *numbers ))

逐条分析这些错误,正好覆盖了 PEP 8 的多个核心规则:

违规现象对应 PEP 8 规则整洁版本的处理
numbers = [1,2,\用反斜杠续行拼接字面量E502(反斜杠续行)、行内缺少空格直接写成完整列表[1, 2, 3, 4]
逗号后无空格(1,2)E231(缺失空白)逗号后统一加一个空格
number_one+number_two运算符两侧无空格W291/E225(运算符两侧缺失空白)运算符两侧各留一个空格
print (add...)函数调用与左括号间有空格E211(括号前多余空白)print(紧贴函数名
( *numbers )星号与括号间有空格E201/E202(括号内侧多余空白)写成(*numbers)

修复后的版本在空行组织上也更讲究:模块级函数之间以两个空行分隔(numbers定义与函数之间、函数与print调用之间),这是 PEP 8 对顶层定义之间的空行要求。修复后的代码在行为上与原始版本完全等价(都输出10),但观感与可维护性天差地别。

行长度与续行策略:告别 E501 不再靠反斜杠

PEP 8 建议每行不超过79 个字符(文档字符串与注释建议 72 字符)。79 个字符的限制看似严苛,却是为多窗口并排编辑、窄屏终端和 Git diff 对齐设计的。现代项目常通过配置文件放宽该限制(例如把line-length调整为 88 或 100),关键是一旦选定就必须全项目统一。

当一行放不下时,正确做法是隐式续行(利用括号内换行),而不是反斜杠。反斜杠续行会带来额外问题:必须在行尾立即换行、容易在\后误留空格导致续行失效(这正是 trailing_whitespace.py 演示的场景)。仓库中 unfashionable.py 的修复版演示了标准写法——把函数参数放进括号内换行,并采用悬挂缩进(hanging indent):

def add_all_numbers_from_collection( number_one, number_two, number_three, number_four ): return number_one + number_two + number_three + number_four

继续换行时的对齐方式有两种主流流派:

  • 悬挂缩进:第一行不写参数,后续参数统一再缩进 4 个空格,与函数体缩进层级区分开(上面代码即此风格);
  • 对齐左括号:第一行写第一个参数,后续参数与左括号后的第一个字符对齐(即"垂直对齐"风格)。

两种都符合 PEP 8,选择哪种取决于团队约定,也可交给格式化工具统一。

命名约定:让变量名"开口说话"

PEP 8 对标识符命名给出了明确约定,这是代码可读性的第一道门面:

标识符类型命名约定示例
变量名、函数名小写加下划线(snake_case)mean、number_one、add_all_numbers_from_collection
常量全大写加下划线MAX_CONNECTIONS、DEFAULT_TIMEOUT
类名驼峰式(PascalCase)class QuadraticSolver
模块名简短小写variance.py、quadratic.py
私有属性单下划线前缀_internal
避免双下划线魔法名、大小写混合的mixedCase__init__仅限特殊方法

仓库中的示例文件本身就是活教材:函数名calculate_variance、add_all_numbers_from_collection、quadratic全部采用 snake_case,描述性强到不需要注释就能读懂意图。同时要避开 PEP 8 点名的反模式:l = 1、O = 2、I = 3(小写 l 与大写 O、I 与数字 0/1 极易混淆),以及单字母变量名(循环计数器i、j除外)。

命名约定的判断(如变量是常量还是普通变量)无法完全自动化,这正是人写规范、工具做辅助的体现——linter 无法替你完成全部命名决策,但它能守住缩进、空白、行长度这些可机械检查的底线。

文档字符串与注释:quadratic.py 的规范示范

PEP 257 规定了 docstring(文档字符串)的书写约定,它回答"这段代码是什么、怎么用"的问题。仓库中的 quadratic.py 给出了教科书式的函数级 docstring:

def quadratic(a, b, c): """Solve quadratic equation via the quadratic formula. A quadratic equation has the following form: ax**2 + bx + c = 0 There always two solutions to a quadratic equation: x_1 & x_2. """ x_1 = (-b + (b**2 - 4 * a * c) ** (1 / 2)) / (2 * a) x_2 = (-b - (b**2 - 4 * a * c) ** (1 / 2)) / (2 * a) return x_1, x_2

注意几个细节:首行以"""直接开头写一句概述,随后空一行再展开细节,最后以"""单独成行收尾;docstring 紧跟函数定义、缩进与函数体一致。PEP 8 建议 docstring 行宽不超过 72 字符。另一个细节藏在返回值里:(-b + (b**2 - 4 * a * c) ** (1 / 2))中算术运算符两侧都有空格,而**幂运算符两侧也留了空格(PEP 8 对幂运算左右是否加空格存在争议,允许不加空格,但保持一致即可)。

documented_module.py 则演示了模块级 docstring——文件第一行直接以三引号字符串说明模块用途。docstring 与普通注释的分工是:docstring 描述"接口"(模块/函数/类怎么用),注释解释"实现"(某段代码为什么这么写)。

用 Ruff 实战 lint 与格式化:让工具守住规范

为什么选择自动工具

手工逐行检查缩进、空白、行长度既枯燥又易漏。现代 Python 工作流的标准答案是:linter 负责发现问题,formatter 负责自动修复。本仓库在 README.md 中明确采用 Ruff 中给出了全仓库共享的规则配置。

仓库的 Ruff 配置解读

查看根目录 pyproject.toml 的[tool.ruff]段,可以学到一套非常实用的配置模式:

[tool.ruff] target-version = "py314" line-length = 79 exclude = [ ".devcontainers", ".github", "migrations", "how-to-indent-in-python/sample_code.py", "agents-md/run1_main.py", "python315-frozendict/", "python315-lazy-imports", "python315-new-features", "ai-benchmark" ] [tool.ruff.lint] select = ["E", "F", "RUF100"] ignore = ["E501", "I"]

要点逐项拆解:

  • line-length = 79:严格按 PEP 8 的 79 字符上限执行(教程语境下的原汁原味配置);
  • target-version = "py314":指定目标 Python 版本,Ruff 据此决定语法检查的激进程度;
  • select = ["E", "F", "RUF100"]:只启用三类规则——E(pycodestyle 错误,即 PEP 8 风格问题,如缺空格、多余空白、缩进错误)、F(pyflakes 逻辑错误,如未使用的导入、未定义名称)、RUF100(检测被无用的noqa注释);
  • ignore = ["E501", "I"]:显式豁免两类规则——E501(行超长)与I(isort 导入排序)。这说明该仓库在保持风格统一的同时,也允许按实际需求对个别规则放行;
  • exclude:将生成脚本、大型实验代码等目录排除在检查范围之外,避免误报或性能浪费;
  • [tool.ruff.lint.isort]的force-sort-within-sections = true:即使不开启I规则,也保留了导入排序的偏好设置。

这套配置可以直接复用到你自己的项目:先窄启规则集、按需豁免、再逐步收紧。

三步实战:检查、修复、验证

对 pep8-beautiful-code 目录中的示例文件,按照仓库 README.md 给出的官方命令流程操作:

第一步,格式化检查(只报告、不改文件):

$ ruff format --check

如果输出类似would reformat pep8-beautiful-code/unfashionable.py,说明 Ruff 认为该文件格式不合规。这正是教程设计的实验场景:先取消unfashionable.py中被注释的"原始代码",让不合规代码真正参与检查。

第二步,执行自动格式化:

$ ruff format

Ruff 会重写所有不合规文件:修正缩进、清理尾随空白、统一运算符两侧空格、规范函数调用写法——unfashionable.py 顶部展示的整洁版本就是这种格式化的结果。

第三步,运行 linter 检查剩余问题:

$ ruff check $ ruff check --fix

ruff check报告E/F类问题;ruff check --fix则自动修复其中可安全修复的部分(如缺失的空格、多余空白)。结合之前提到的规则码,你会看到输出中带有E231、W291、E211等编号——它们与 trailing_whitespace.py 注释里的w291属于同一套 pycodestyle 规则体系,学会读这些编号,就能精准定位每一条风格问题。

完成上述步骤后,mixed_indentation.py中的TabError隐患、trailing_whitespace.py中的行尾空白都会被一并消除。你还可以在本仓库的任意子目录重复这套流程,对比格式化前后的 diff,直观感受工具的价值。

收尾:把"漂亮代码"变成日常习惯

回顾整个实战,PEP 8 的价值不在于死记硬背每一条规则,而在于建立三件套习惯:

  1. 写:从源头遵守 4 空格缩进、79 字符行宽、snake_case 命名与规范 docstring,让代码"天生好看";
  2. 查:用ruff check这类 linter 定期扫描,把风格问题扼杀在提交之前;
  3. 修:用ruff format自动格式化,把不修边幅的代码一键打磨平整。

仓库的 pep8-beautiful-code 目录为这个过程提供了可反复练习的素材——取消注释、运行 lint、观察报告、自动修复、对比结果。当你能够熟练地把unfashionable.py从"能跑但难看"改造成 整洁版本 时,PEP 8 的核心理念——代码的受众首先是读者,其次才是解释器——就已经内化成你的编码直觉了。

  • 示例工程
  • 教程

【免费下载链接】materials

Bonus materials, exercises, and example projects for our Python tutorials

项目地址:https://gitcode.com/gh_mirrors/ma/materials
点击查看免费下载

相关推荐

上一篇:Unstract Test Rig 深度指南:用统一测试框架编排跨服务集成与端到端测试
下一篇:es-toolkit 的 uniqWith 使用指南:基于自定义比较函数的数组去重

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询