python coding 代码规范
#
# Python coding时该注意的代码规范
作为一名程序员,我们不可避免的要大量coding,虽然有了AI的帮助,但是并不是所有环境下AI都适用(某大厂甚至还限制使用外部ai哼),所以难免需要自己来coding,当代码的行数一多,阅读+理解代码便成为一个NP难的问题(因为debug是NP难的问题),所以为了让别人(和我们自己)更容易看懂我们的代码,也让代码看上去更整洁好看,我们需要一个代码规范,于是让我们掌声有请——
PEP 8 – Style Guide for Python Code
PEP 是 Python Enhancement Proposal(Python 增强提案)的缩写,而第 8 号提案专门讨论的是 Style Guide for Python Code。它由 Python 的创始人 Guido van Rossum 等人在 2001 年编写,核心哲学只有一句话:“代码被阅读的次数远多于被编写的次数”(Readability counts)。
首先要提一嘴,我们其实有现成的代码格式化工具:Black (opens new window),这里仅仅提供教程,大家感兴趣的可以自行配置(玛卡巴卡已经配置了嘻嘻)
下面分几个板块来讲讲 PEP 8 主要的规范包含哪些
# 1. 代码布局(缩进、行宽、换行、空行)
这一块决定了代码“第一眼是否顺”,也是团队协作里最能减少争论的一部分。
# 1.1 缩进:4 个空格,别用 Tab
- 每层缩进 4 个空格。
- 不要混用 Tab 与空格(IDE 里把 Tab 自动转换为空格最省事)。这是因为Tab只是一个逻辑字符在不同IDE(代码编辑器)中表示的宽度可能不一样,这样在不同的编辑器中打开时代码可能就格式不一样,解码的时候可能就会出问题
推荐
def foo(x):
if x > 0:
return x
return 0
2
3
4
不推荐(Tab/缩进不一致)
def foo(x):
\tif x > 0:
\t return x
2
3
# 1.2 行宽:尽量不超过 79(注释/文档 72)
- 代码行建议 <= 79 字符。
- docstring 和长注释建议 <= 72 字符(更适合阅读器/终端)。
# 1.3 长表达式换行:用括号,避免反斜杠
- 换行优先用
() [] {}的隐式换行:Python 解释器有一个特性,当它检测到括号(小括号 ()、中括号 []、花括号 {})未闭合时,会自动将后续行视为当前行的延续。这种方式被称为“隐式续行”。。 - 反斜杠
\容易出坑(尾随空格、合并冲突等),尽量避免- 不可见的语法错误(尾随空格): 反斜杠必须是该行的最后一个字符。如果在反斜杠后面不小心输入了一个空格或制表符,Python 解释器会抛出 SyntaxError: unexpected character after line continuation character。由于空格是不可见的,这类错误往往极难排查。
- 维护困难: 在后续的代码重构或合并冲突(Merge Conflict)中,反斜杠极易被误删或遗漏,导致逻辑中断。
推荐(括号隐式换行)
total = (
base_price
+ shipping_fee
- discount
+ tax
)
2
3
4
5
6
不推荐(反斜杠换行)
total = base_price + shipping_fee \
- discount + tax
2
# 1.4 空行:用来“分组”,让结构一眼清楚
高频规则:
- 顶层
def/class之间:空 2 行- 两个顶级类之间
- 两个顶级函数之间
- 顶级类与顶级函数之间
- 类内部方法之间:空 1 行
- 逻辑段落之间:适当空 1 行,这类似于文章中的分段。如果一个函数较长,将其拆分为“数据准备”、“核心计算”、“结果返回”等逻辑段落,并用空行分隔,可以显著提升可读性。
推荐
import os
import sys
class UserService:
def create_user(self, name):
return {"name": name}
def delete_user(self, user_id):
...
2
3
4
5
6
7
8
9
10
# 2. 导入(imports)顺序与写法
导入写得好,文件“可读性”和“可维护性”都会明显提升。
# 2.1 导入分组顺序:标准库 → 第三方 → 本地
并且每组之间空一行。
推荐
import json
from pathlib import Path
import requests
from myapp.config import settings
from myapp.utils import slugify
2
3
4
5
6
7
小tips: 可以通过运行
pip show [package_name]来快速检验
如果是第三方库,会显示该库的版本、摘要、作者及安装位置(通常在 site-packages 目录下) 如果是标准库,会提示 Package(s) not found,因为标准库不是通过 pip 安装的
# 2.2 避免通配符导入:from x import *
通配符会污染命名空间,读代码时很难知道一个名字来自哪里。
推荐
from math import sqrt
不推荐
from math import *
# 2.3 导入要放在文件顶部
除了少数情况(比如为了避免循环依赖、或性能原因的延迟导入)之外,都放顶部。
# 3. 空格使用(最常见的“格式化争议”)
这一块非常高频,且最容易被自动格式化工具统一
# 3.1 运算符两边加空格(赋值、比较、算术等)
推荐
x = 1
y = x + 2
ok = (a == b)
2
3
不推荐
x=1
y=x+2
ok=(a==b)
2
3
# 3.2 函数调用/索引的括号内一般不加空格
在处理括号时,应遵循“边缘紧凑、元素分隔”的原则。这意味着括号与其包含的第一个/最后一个字符之间不留空格,但多个参数/元素之间必须通过空格保持清晰。
- 括号边缘:紧贴起始括号之后和结束括号之前不加空格。
- 参数分隔:在函数调用、定义或列表/元组中,逗号后面必须跟一个空格,但逗号前面严禁有空格。
- 参数赋值:在函数定义(默认参数)或函数调用(关键字参数)中,
=两侧不加空格。 - 调用关联:函数名与左括号之间、索引变量名与左中括号之间不加空格。
推荐
# 括号边缘紧凑,参数间保留 1 个空格
foo(a, b)
my_list = [1, 2, 3]
# 索引与函数名紧贴括号
items[0]
result = calculate_value(x)
# 默认参数/关键字参数等号两侧不加空格
def complex_func(arg1, default_val=10):
return arg1 * default_val
complex_func(5, default_val=20)
2
3
4
5
6
7
8
9
10
11
12
13
不推荐
# 错误:括号边缘多余空格
foo( a, b )
items[ 0 ]
# 错误:逗号后缺少空格(过于拥挤)
foo(a,b)
# 错误:函数名与括号间、等号两侧多余空格
foo (a, b)
def func(arg = 5):
pass
2
3
4
5
6
7
8
9
10
11
# 3.3 逗号、冒号的空格:后有空格,前不加
推荐
data = {"a": 1, "b": 2}
for i in range(1, 10):
...
2
3
不推荐
data = {"a":1,"b":2}
for i in range(1,10):
...
2
3
# 3.4 关键字参数 = 两边不加空格
这是很多人会写错的点:关键字参数不要写成 x = 1 这种。
推荐
connect(host="localhost", port=5432)
不推荐
connect(host = "localhost", port = 5432)
# 4. 命名规范(变量、函数、类、常量)
命名一旦统一,项目阅读成本会直线下降(其实主要是看起来确实更容易了!)
# 4.1 常见命名风格速记
在 Python 中,命名风格不仅是视觉上的统一,更是代码语义的一部分。通过不同的命名格式,开发者可以瞬间识别出一个标识符是类、函数还是常量,从而降低理解成本。
# 4.1.1 变量、函数与方法:lower_snake_case
这是 Python 中最常用的风格。所有字母小写,单词之间用下划线连接。
- 设计逻辑:下划线增加了单词间的视觉距离,使得长命名在快速阅读时依然清晰。
- 适用范围:普通变量、全局函数、类的方法(Method)、函数参数。
# 4.1.2 类:CapWords
每个单词的首字母大写,不使用下划线。
- 设计逻辑:这种风格(也称大驼峰式)能将“类”(对象的蓝图)与“函数”(动作)在视觉上显著区分开。
- 适用范围:异常类、基类、具体实现类。
# 4.1.3 模块级常量:UPPER_SNAKE_CASE
所有字母大写,单词之间用下划线连接。
- 设计逻辑:全大写字母在代码中非常醒目,起到“警示”作用,提示开发者这是一个不应被修改的固定值。
- 适用范围:配置参数、数学常数、固定阈值。
# 4.1.4 内部使用与冲突处理:下划线的妙用
- 单前导下划线
_name:这是一种“弱内部使用”标识。它告诉其他开发者:“这是一个私有属性或内部工具函数,请不要在类或模块外部直接调用它。” 虽然 Python 语法不强制限制访问,但这是社区通用的尊重契约。 - 单末尾下划线
name_:当你想用的变量名恰好是 Python 的保留关键字(如class,list,type)时,在末尾加一个下划线是官方推荐的规避方案。
示例
# 常量:全局可见,不可修改
MAX_RETRY_COUNT = 5
DEFAULT_TIMEOUT = 30.0
# 类:首字母大写
class DataProcessor:
def __init__(self):
# 内部变量:提示外部不要直接访问
self._is_initialized = False
# 方法:小写加下划线
def process_raw_data(self, data_list):
pass
# 函数:小写加下划线
def fetch_user_by_id(user_id, class_="Standard"):
# class_ 避免了与关键字 class 冲突
pass
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
命名风格对照表
| 类型 | 风格 | 示例 |
|---|---|---|
| 变量/函数/方法 | 小写下划线 | calculate_total(), user_name |
| 类 | 大驼峰 | UserSession, HttpRequest |
| 常量 | 大写下划线 | API_KEY, MAX_THREADS |
| 内部/私有 | 前导下划线 | _internal_method(), _cache |
| 避开关键字 | 末尾下划线 | id_, type_, input_ |
# 4.2 避免 “l / O / I” 这种易混淆命名
例如 l(小写 L)在某些字体里像 1,O 像 0。
# 5. 注释与 Docstring(让代码“可维护”)
这部分是写给“未来的同事和未来的你”的🤓
# 5.1 注释写什么:解释“为什么”,不是复述“是什么”
推荐(解释原因/约束)
# 使用 UTC,避免夏令时导致的跨天 bug
expires_at = datetime.now(timezone.utc) + timedelta(hours=1)
2
不推荐(复述代码)
# 给 expires_at 加一小时
expires_at = datetime.now(timezone.utc) + timedelta(hours=1)
2
# 5.2 Docstring:给模块/类/函数写清楚“用途、参数、返回、异常”
PEP 8 更偏“形式与风格”,但日常你至少保持:
- 一句话概述
- 需要时补充参数/返回含义(团队有模板就跟模板)
推荐
def parse_user_id(raw: str) -> int:
"""Parse user id from a string.
Args:
raw: User id string, e.g. "123".
Returns:
Parsed user id as int.
Raises:
ValueError: If raw is not a valid integer.
"""
return int(raw)
2
3
4
5
6
7
8
9
10
11
12
13
# 6. 逗号结尾(Trailing comma)与多行结构
这条很“工程化”,能显著减少 diff 噪音(主要是多人维护的团队合作项目)
# 6.1 多行列表/字典/参数建议保留尾逗号
推荐
items = [
"apple",
"banana",
"orange",
]
config = {
"host": "localhost",
"port": 5432,
}
2
3
4
5
6
7
8
9
10
好处:以后新增一行不会改动上一行,Git diff 更干净。
# 7. 条件判断与 Pythonic 写法(PEP 8 常强调的“可读性”)
PEP 8 的精神是——可读性优先!
# 7.1 与 None 比较用 is / is not
推荐
if value is None:
...
2
不推荐
if value == None:
...
2
# 7.2 布尔判断别写 == True/False
推荐
if enabled:
...
if not enabled:
...
2
3
4
不推荐
if enabled == True:
...
if enabled == False:
...
2
3
4
# 7.3 成员判断用 in
推荐
if key in mapping:
...
2
# 8. 异常处理风格(高频且影响质量)
异常是对“可维护性”至关重要,玛卡巴卡就很少写try-catch捕获异常x(可能也是没有做过大项目导致的)
# 8.1 不要裸 except:
在 Python 中,except: 实际上等同于 except BaseException:。这种“全量捕获”会产生严重的副作用,导致程序失控或 Bug 被隐藏。
- 屏蔽系统信号:它会捕获
KeyboardInterrupt(Ctrl+C)和SystemExit。这意味着你无法通过常规手段强制停止一个死循环程序。 - 掩盖逻辑错误:它会吞掉所有的异常,包括变量名拼错(
NameError)或属性不存在(AttributeError)。这会让原本明显的逻辑 Bug 变得极难调试。 - 最佳实践:始终指明具体的异常类型。如果确实需要捕获所有业务错误,请使用
except Exception:,它会跳过系统级的强制退出信号。
推荐
try:
n = int(raw)
except ValueError: # 精确捕获预期的转换错误
n = 0
# 或者在需要捕获绝大多数错误时
try:
do_something()
except Exception as e: # 允许 Ctrl+C 退出,同时记录错误
logging.error(f"Error: {e}")
2
3
4
5
6
7
8
9
10
不推荐
try:
n = int(raw)
except: # 裸捕获:Ctrl+C 失效,且会隐藏其他逻辑 Bug
n = 0
2
3
4
| 捕获方式 | 捕获范围 | 评价 |
|---|---|---|
except: | 所有(含系统信号) | 禁止使用,会导致程序无法停止 |
except Exception: | 所有业务异常 | 慎用,仅限顶层逻辑监控 |
except SpecificError: | 仅指定的错误 | 推荐,最安全、最易于调试 |
# 8.2 异常别太宽;能具体就具体
在 Python 中,异常是分层级的。Exception 是几乎所有常规错误的“祖先”。如果你捕获了 Exception,就意味着你同时捕获了 IndexError、KeyError、TypeError、NameError 等几十种完全不同的错误。
# 8.2.1. 避免“张冠李戴”的错误处理
每种异常通常对应不同的解决办法。
- 如果是
FileNotFoundError,你可能想提示用户检查路径。 - 如果是
PermissionError,你可能想提示用户提升权限。 - 如果你统一捕获
Exception并提示“文件出错”,当程序其实是因为“内存溢出”或“变量名写错”而崩溃时,这个提示会严重误导用户。
# 8.2.2. 保护真正的 Bug 不被掩盖
这是最危险的一点。请看下面的例子:
# 糟糕的写法:范围太宽
try:
user = get_user_info()
age = user["age"] + 1 # 如果 user 字典里没有 "age",会报 KeyError
except Exception:
age = 0 # 你本意是想处理 KeyError
2
3
4
5
6
隐藏的危机:如果 get_user_info() 函数内部因为某个逻辑写错报了 NameError,或者 user["age"] 的值是字符串导致加法报了 TypeError,这些真正的代码 Bug 都会被 except Exception 默默吞掉,并给 age 赋值为 0。程序会继续带着错误的数据运行,直到在更远的地方引发更难排查的崩溃。
什么时候可以用 Exception?
- 顶层监控(Global Catch-all):在 Web 框架或大型系统的最外层,为了防止一个小的子功能报错导致整个服务器宕机,会捕获
Exception。 - 记录并重新抛出:你想在程序崩溃前把错误堆栈记入日志,记录完后再用
raise把错误原样抛出。
# 9. 字符串引号、格式化与可读性
这块更多是“统一风格”,不过也看自己的习惯咯
# 9.1 单引号/双引号:项目内一致即可
PEP 8 不强制单/双引号,但强调一致性。
# 9.2 字符串拼接优先用 f-string / .format,避免 + 乱拼
推荐
name = "Alice"
msg = f"Hello, {name}"
2
不推荐(大量 + 会变难读)
msg = "Hello, " + name + "!"
# 10. 一些高频需要记住的PEP 8 规范
这一块给你做总结页/背诵页很方便:
- 缩进:4 空格;不要混 Tab
- 行宽:代码 79,注释/docstring 72
- 换行:优先用括号隐式换行,不用
\ - 空行:顶层函数/类 2 行;类方法之间 1 行
- 导入:标准库 → 第三方 → 本地;组间空行;避免
import * - 空格:运算符两边加空格;关键字参数
=不加空格 - 命名:函数/变量
snake_case;类CapWords;常量UPPER_CASE - 判断:
is None;不用== True/False - 异常:别裸
except:;尽量捕获具体异常 - 多行容器:建议保留尾逗号减少 diff 噪音