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()