代码会被人读很多次:同事 review、未来的自己调试、新人接手。风格混乱的代价:每次 merge 都是格式冲突、阅读成本高、bug 藏在难看的代码里更难找。
直觉类比:风格规范像"团队的语言规范"——大家都说普通话、用统一术语,沟通才高效。格式化工具则是"自动纠错机":机器统一格式,人只负责写对逻辑。
💡 关键直觉:规范 = 工具约束 + 约定俗成。能用工具自动化的(格式、导入顺序)交给工具;需要人遵守的(命名、注释、结构约定)靠规范文档与 review。
pip install black flake8 isort mypy
# pyproject.toml(black 配置) [tool.black] line-length = 100 target-version = ['py312'] [tool.isort] profile = "black" line_length = 100 [tool.mypy] python_version = "3.12" ignore_missing_imports = true
black app/ tests/ # 格式化整个目录 black --check app/ # 只检查不修改(CI 用)
black 是"不可争论的格式化器"——它做出的决定就是标准,省去风格争论:
# 格式化前 def login(username,password,remember=False): user=User.query.filter_by(username=username).first() return user # 格式化后 def login(username, password, remember=False): user = User.query.filter_by(username=username).first() return user
isort app/ tests/
自动分组排序:标准库 → 第三方 → 本地模块,每组按字母序:
import os import sys import flask from flask_sqlalchemy import SQLAlchemy from .extensions import db from .models import User
flake8 app/ tests/
检查:未使用变量、行太长、未定义名字、复杂度过高(可配置)等。常用忽略配置(setup.cfg):
[flake8] max-line-length = 100 extend-ignore = E203, W503 exclude = migrations,venv
mypy app/
配合类型注解,提前发现类型错误(把 None 当字符串用等):
from typing import Optional def get_user(username: str) -> Optional[User]: return User.query.filter_by(username=username).first()
| 对象 | 规范 | 示例 |
|---|---|---|
| 变量/函数 | snake_case | get_user, created_at |
| 类 | PascalCase | User, RegisterForm |
| 常量 | UPPER_SNAKE | MAX_RETRIES |
| 模块 | snake_case | blueprints/auth.py |
| 蓝图变量 | 以 bp 结尾 | main_bp, auth_bp |
| 布尔变量 | 用 is/has | is_active, has_permission |
# 错误:做了啥(代码本身看得见) # 循环遍历用户 for u in users: ... # 正确:为什么这么做 # 生产环境的 Redis 键必须带环境前缀,避免多环境共用时串数据 key = f"{current_app.config['ENV_PREFIX']}:user:{uid}"
# 加 1)、不要留大段注释掉的代码(用 Git 历史)。login、register、list_posts;auth.login、main.index;/user-profile(而非下划线),endpoint 用下划线;SECRET_KEY、SQLALCHEMY_DATABASE_URI;users),模型类单数(User);/users/<int:id> 吞掉 /users/new)。约定式提交(Conventional Commits):
feat: 新增用户资料编辑接口 fix: 修复登录后跳转 404 docs: 更新部署文档 refactor: 重构蓝图注册逻辑 test: 补充 CSRF 相关测试
原则:提交小而聚焦、一条提交一件事、信息说"改了什么、为什么"。
# .github/workflows/lint.yml(示例) steps: - uses: actions/checkout@v4 - run: pip install black flake8 isort mypy - run: black --check app/ tests/ - run: isort --check-only app/ tests/ - run: flake8 app/ tests/ - run: mypy app/
规范不合格不让合并——让工具当"风格警察",人专注逻辑。
| 误区 | 现象 | 正解 |
|---|---|---|
| 手调格式 | merge 冲突不断 | black 统一,别手改 |
| 一行超长 | flake8 报 E501 | 换行/重构,不是忽略 |
| 命名随意 | 读不懂 | 按规范命名 |
| 注释写"是什么" | 噪音 | 写"为什么" |
| 提交一大坨 | 无法回滚定位 | 小而聚焦 |
| 规范只在本地 | 有人绕过 | CI 强制检查 |
# 1. 写完功能后 black app/ isort app/ flake8 app/ mypy app/ # 2. 全部通过后 git add -A git commit -m "feat: 新增用户资料接口" git push
模板习惯:把这三条命令放进 pre-commit 钩子(pre-commit 框架),每次提交自动执行——规范变成肌肉记忆。
规范不是"死规矩",每一条背后都有代价与收益。
第一,规范的本质是"降低沟通成本"。 代码的阅读次数是书写次数的数十倍。统一风格后,读者不需要"解码"每个人的个人习惯,直接把注意力放在逻辑上。规范节省的是团队的集体阅读时间——这比任何单个开发者的书写速度都值钱。所以规范的收益公式是:阅读频率 × 团队规模 × 节省的每行理解时间。
第二,工具的边界:能自动化就不靠自觉。 格式化(black)、导入排序(isort)、基础检查(flake8)全部交给工具+CI,人只负责写对逻辑。工具把规范变成"默认行为"而不是"道德要求"——道德靠自觉会滑坡,工具不会。这也是为什么"规范必须进 CI":不强制就没有规范。
第三,类型注解的投资回报。 mypy 的短期成本是"写代码时要多打几个字",长期收益是"重构时编译器帮你抓错"。对 Python 这种动态语言,类型注解是"让大型项目可维护"的关键工具之一。建议从公共接口开始(函数签名、模型字段、API 返回),逐步覆盖到内部实现——不必一步到位。
第四,命名是最大的注释。 好名字让代码自解释:get_active_users 比 func1 清楚一百倍;is_paid 比 flag 清楚一百倍。命名要表达"意图"而非"实现"(calculate_total 而不是 loop_and_sum)。代码审查时,把"这个名字看不懂"当成问题提出——命名质量是代码质量的第一直观指标。
第五,注释的"为什么"原则的边界。 "为什么"注释适用于:看似不合理的决策("这里不用缓存,因为数据变化极频繁")、复杂算法的思路、外部约束("第三方 API 限制每秒 10 次")。不适用:很明显的行为("循环遍历列表")、会过时的描述("这里计算价格"——价格逻辑变了注释就骗人了)。注释会骗人,代码不会——注释要写"代码表达不了的信息"。
第六,从个人规范到团队规范。 团队规范不是一个人定的:通过 code review 讨论形成共识、用工具固化可自动化的部分、文档只记录"工具管不了"的约定(命名规范、分支策略、提交格式)。规范的迭代:每个季度审视一次"哪些约定没人遵守"——没人遵守的约定要么废掉,要么用工具强制。规范是活文档,跟着团队一起演进。