Python 编码规范完全指南
基于 PEP 8、PEP 257、PEP 484 等核心规范,系统化整理 Python 编码最佳实践
1. 代码布局
缩进、空行、行长度、空白符的规范使用
🏗️ 缩进
必须✓ 推荐
# 使用 4 个空格 def get_user(user_id): user = db.query(user_id) if user: return user return None
✗ 避免
# Tab 或 2 空格(禁止) def get_user(user_id): ··user = db.query(user_id) ····if user: ········return user
📏 最大行长
必须所有行限制在 79 个字符以内(硬性),理想目标 72 个字符(软性)。
Python
# 使用括号换行(推荐) result = some_function( arg1, arg2, arg3, arg4, arg5 ) # 或反斜杠续行 result = some_function(arg1, arg2, \ arg3, arg4)
↕️ 空行规则
应该| 场景 | 空行数 | 说明 |
|---|---|---|
| 顶级函数/类之间 | 2 行 | 模块级定义的逻辑分隔 |
| 类方法之间 | 1 行 | 类内部方法的分隔 |
| import 之后 | 1 行 | 与函数定义之间分隔 |
〰️ 空白符使用
应该✓ 推荐
# 赋值运算符周围加空格 x = 1 y = x + 1 # 默认值参数 def func(x=0): pass
✗ 避免
# 多余的空格 x =1 # 赋值周围不加 y=x+1 # 操作符周围要加 # 默认值参数(禁止) def func(x = 0): pass
2. 导入规范
import 顺序、分组组织、相对导入的规则
📊 Import 分层结构
📑 标准库 → 第三方 → 本地
必须
Python
# 1. 标准库(无需安装) import os import sys import json from typing import List, Dict # 空行分隔 # 2. 第三方库(需安装) import requests import numpy as np from pydantic import BaseModel # 空行分隔 # 3. 本地应用(相对导入) from . import utils from .. import config
🔤 import vs from
应该| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 模块名短,且多次使用 | import os |
避免命名空间污染 |
| 导入具体成员 | from os import path |
代码更简洁 |
| 避免名称冲突 | from os import path as os_path |
重命名显式清晰 |
避免通配符导入
from module import * 禁止使用,它会让命名空间混乱,难以静态分析。
3. 命名规范
变量、函数、类、常量、模块的命名约定
🌳 命名风格对照
命名对照表
必须| 元素 | 风格 | 示例 | 说明 |
|---|---|---|---|
| 模块 | snake_case |
my_module.py |
全小写,可带下划线 |
| 类名 | CamelCase |
class UserProfile |
首字母大写 |
| 函数 | snake_case |
def get_user() |
全小写加下划线 |
| 变量 | snake_case |
user_name = "Tom" |
同函数命名 |
| 常量 | SCREAMING_SNAKE |
MAX_RETRIES = 3 |
全大写加下划线 |
| 私有属性 | _snake_case |
self._cache = {} |
单下划线前缀 |
| 特殊属性 | __snake_case__ |
__name__ |
双下划线前后缀 |
私有保护机制
应该Python
class UserService: """用户服务类""" def __init__(self): self._db_connection = None # 受保护属性(约定) self.__secret_key = "xxx" # 私有属性(名称混淆) def _protected_method(self): """子类可访问""" pass def __private_method(self): """名称被混淆""" pass
单下划线
双下划线
_:约定私有,外部可访问但不推荐双下划线
__:名称混淆,触发 Python 名称修饰(name mangling)
4. 文档字符串
Docstring 的编写规范(PEP 257)
单行 vs 多行
必须✓ 单行 Docstring
class AppConfig: """应用配置类"""
✓ 多行 Docstring
class AppConfig: """应用配置类 负责管理应用程序的所有配置项, 支持从环境变量和配置文件加载。 """
三大主流风格
应该Google 风格(最常用)
Python
class User: """用户信息类 Attributes: name: 用户姓名 age: 用户年龄 """ def __init__(self, name: str, age: int): """初始化用户 Args: name: 用户姓名 age: 用户年龄 """ self.name = name self.age = age
NumPy 风格(学术/数据科学)
Python
def process_data(data, mode: str = "auto"): """处理数据数组 Parameters ---------- data : np.ndarray 输入数据数组 mode : str, optional 处理模式,默认 'auto' Returns ------- np.ndarray 处理后的数据 """ pass
Sphinx 风格(大型项目)
Python
def calculate(x: float, y: float): """计算两数之和 :param x: 第一个加数 :type x: float :param y: 第二个加数 :type y: float :return: 两数之和 :rtype: float """ return x + y
项目级统一风格:无论选择哪种风格,保持团队统一即可。推荐新项目使用 Google 风格,易读且 IDE 支持好。
5. 类型注解
类型提示的规范使用(PEP 484)
基础类型注解
应该Python
# 变量注解 name: str = "Alice" age: int = 25 scores: list[float] = [90.5, 85.0] # 函数注解 def greet(name: str, age: int = 0) -> str: return f"Hello, {name}!" # 复杂类型 users: dict[str, int] = {"Alice": 25}
Optional / Union / Any / Callable
应该Python
from typing import Optional, Union, Any, Callable # Optional: 可能为 None def find_user(user_id: int) -> Optional[dict]: return None # Union: 联合类型(多种可能) result: Union[int, str] = 42 # Any: 任意类型(尽量少用) data: Any = fetch_data() # Callable: 可调用对象 callback: Callable[[int, int], int] = lambda x, y: x + y
Python 3.10+ 可使用更简洁的语法:
result: int | str 替代 Optional[Union[int, str]]def func(x: int | str) -> int | None
6. 异常处理
异常捕获、抛出、自定义的规范
try...except 最佳实践
必须Python
# ✓ 捕获具体异常 try: result = 10 / 0 except ZeroDivisionError as e: print(f"错误: {e}") # ✗ 避免裸 except(会捕获 SystemExit, KeyboardInterrupt) try: result = 10 / 0 except: # 禁止! pass # ✓ 多个异常处理 try: open("file.txt") except (FileNotFoundError, PermissionError) as e: print(f"文件错误: {e}") # ✓ 异常链(Python 3+) try: open("missing.txt") except FileNotFoundError as e: raise ValueError("无法加载配置") from e
自定义异常
应该Python
# ✓ 自定义异常应继承自 Exception 或其子类 class ValidationError(Exception): """验证错误""" def __init__(self, field: str, message: str): self.field = field self.message = message super().__init__(message) # ✓ 使用自定义异常 raise ValidationError("email", "邮箱格式不正确")
7. 函数设计
函数设计原则、参数设计、返回值处理
核心原则
必须| 原则 | 说明 | 示例 |
|---|---|---|
| 单一职责 | 每个函数只做一件事 | get_user() + save_user() |
| 简洁性 | 尽量控制在 30 行以内 | 拆分复杂逻辑 |
| 明确命名 | 函数名表达动作 | calculate_total() |
| 避免副作用 | 纯函数更易测试 | get_date() 不修改全局状态 |
参数与返回值
应该Python
# ✓ 参数过多时使用 dataclass from dataclasses import dataclass @dataclass class UserConfig: name: str age: int email: Optional[str] = None def create_user(config: UserConfig) -> User: pass # ✓ 错误处理优先使用异常 def parse_config(path: str) -> Config: if not path.exists(): raise FileNotFoundError(path) # ... # ✓ 返回值一致 def process(data): return result # 不要有时 return None
8. 类设计
类的组织、继承、组合的设计原则
类的组成顺序
必须Python
class MyClass: """类文档字符串""" # 1. 类变量(公开) default_timeout = 30 # 2. 实例变量(公开) # 3. 实例变量(受保护 _xxx) # 4. 实例变量(私有 __xxx) def __init__(self, ...): self.name = name # 5. 特殊方法(__str__, __repr__ 等) def __repr__(self) -> str: return f"MyClass(name={self.name!r})" # 6. 公开方法 # 7. 受保护方法 _xxx # 8. 私有方法 __xxx # 9. 静态方法 / 类方法 @classmethod def from_dict(cls, data: dict): return cls(**data)
组合 vs 继承
应该✓ 优先组合
class Engine: def start(self): pass class Car: # 组合:Car 有一个 Engine def __init__(self): self._engine = Engine()
⚠ 谨慎继承
# 继承层次过深会难以维护 class A: pass class B(A): pass class C(B): pass class D(C): pass
9. 测试规范
单元测试、集成测试的命名与组织
测试文件组织
必须目录结构
project/
├── src/
│ └── mymodule.py
└── tests/
├── __init__.py
├── test_mymodule.py # 模块测试
├── test_integration.py # 集成测试
└── conftest.py # pytest 配置
测试函数命名
必须Python
# ✓ 使用 test_ 前缀,命名描述测试场景 def test_user_create_with_valid_data(): pass def test_user_create_with_duplicate_email_raises_error(): pass # ✓ 清晰的错误信息 def test_user_age_is_positive(): user = User(age=-5) assert user.age > 0, "年龄必须为正数"
AAA 模式(Arrange-Act-Assert)
应该Python
def test_user_login(): # Arrange - 准备测试数据 user = create_test_user("test@example.com", "password123") client = TestClient() # Act - 执行被测操作 response = client.post("/login", json={ "email": "test@example.com", "password": "password123" }) # Assert - 验证结果 assert response.status_code == 200 assert "token" in response.json()