Veloris.
返回索引
最佳实践 2026-02-14

Python代码规范与最佳实践:PEP 8不是教条,但这些规则值得遵守

2 分钟
471 words

Python代码规范与最佳实践:PEP 8不是教条,但这些规则值得遵守

良好的代码规范不仅让代码更易读、易维护,也是团队协作的基础。本篇介绍Python的PEP 8规范和常用的最佳实践。


1. PEP 8代码风格

# ===== 缩进 =====
# 使用4个空格,不要用Tab
def function():
    if True:
        print("正确缩进")

# 续行对齐
result = some_function(
    arg1, arg2,
    arg3, arg4
)

# 或悬挂缩进
result = some_function(
    arg1, arg2, arg3, arg4
)

# ===== 行长度 =====
# 每行最多79字符(代码),72字符(文档/注释)
# 长行可以用括号换行
long_string = (
    "这是一个很长的字符串,"
    "可以用括号来换行"
)

# ===== 空行 =====
# 顶级定义之间空两行
class MyClass:
    pass


def my_function():
    pass


# 类内方法之间空一行
class AnotherClass:
    def method1(self):
        pass

    def method2(self):
        pass

# ===== 导入 =====
# 导入应该分组,每组之间空一行
# 1. 标准库
# 2. 第三方库
# 3. 本地模块

import os
import sys

import numpy as np
import pandas as pd

from mypackage import mymodule

# 不要使用 from module import *
# 每个导入单独一行
import os
import sys
# 而不是 import os, sys

# ===== 空格 =====
# 括号内不要空格
spam(ham[1], {eggs: 2})  # 正确
spam( ham[ 1 ], { eggs: 2 } )  # 错误

# 逗号后面加空格
x, y = 1, 2

# 运算符两边加空格
x = 1 + 2
y = x * 2

# 但函数参数默认值不加空格
def func(arg1, arg2=None):
    pass

# 冒号用于切片时不加空格
lst[1:3]
lst[::2]

2. 命名规范

# ===== 变量和函数:小写下划线 =====
user_name = "张三"
total_count = 100

def calculate_average(numbers):
    return sum(numbers) / len(numbers)

# ===== 常量:大写下划线 =====
MAX_SIZE = 1024
DEFAULT_TIMEOUT = 30
PI = 3.14159

# ===== 类名:大驼峰 =====
class UserAccount:
    pass

class HTTPConnection:
    pass

# ===== 模块名:小写,可用下划线 =====
# my_module.py
# utils.py
# data_processor.py

# ===== 私有成员:单下划线前缀 =====
class MyClass:
    def __init__(self):
        self._internal_value = 0  # 内部使用
    
    def _helper_method(self):  # 内部方法
        pass

# ===== 名称修饰:双下划线前缀 =====
class Parent:
    def __init__(self):
        self.__private = 1  # 会被修饰为 _Parent__private

# ===== 魔术方法:双下划线包围 =====
class Container:
    def __init__(self):
        self.items = []
    
    def __len__(self):
        return len(self.items)
    
    def __str__(self):
        return f"Container({self.items})"

# ===== 避免的命名 =====
# 不要用单字符 l, O, I(容易混淆)
# 不要用内置名称:list, dict, str, id, type
# 不要用拼音,用英文

# ===== 好的命名示例 =====
# 动词开头的函数名
def get_user_by_id(user_id):
    pass

def calculate_total_price(items):
    pass

def is_valid_email(email):  # 返回布尔值用is/has/can开头
    pass

def has_permission(user, action):
    pass

# 名词的变量名
user_list = []
error_message = ""
file_path = ""

3. 代码组织

# ===== 文件结构 =====
"""
模块文档字符串
"""

# 1. 导入
from __future__ import annotations  # future导入(如果需要)

import os  # 标准库
import sys

import numpy as np  # 第三方库

from .utils import helper  # 本地导入

# 2. 常量
DEFAULT_VALUE = 100
MAX_RETRIES = 3

# 3. 类型别名
UserDict = dict[str, any]

# 4. 异常类
class CustomError(Exception):
    pass

# 5. 辅助函数
def _helper():
    pass

# 6. 主要类
class MainClass:
    pass

# 7. 主要函数
def main():
    pass

# 8. 入口点
if __name__ == '__main__':
    main()

# ===== 函数组织 =====
def well_organized_function(arg1, arg2, arg3=None):
    """
    函数文档字符串
    """
    # 1. 参数验证
    if arg1 is None:
        raise ValueError("arg1不能为None")
    
    # 2. 初始化
    result = []
    
    # 3. 主要逻辑
    for item in arg1:
        processed = process(item)
        result.append(processed)
    
    # 4. 返回结果
    return result

# ===== 类组织 =====
class WellOrganizedClass:
    """类文档字符串"""
    
    # 1. 类属性
    class_attribute = "value"
    
    # 2. __init__
    def __init__(self, value):
        self.value = value
    
    # 3. 特殊方法
    def __str__(self):
        return f"WellOrganizedClass({self.value})"
    
    def __repr__(self):
        return self.__str__()
    
    # 4. 属性
    @property
    def computed_value(self):
        return self.value * 2
    
    # 5. 公共方法
    def public_method(self):
        pass
    
    # 6. 私有方法
    def _private_method(self):
        pass
    
    # 7. 类方法
    @classmethod
    def from_string(cls, s):
        return cls(int(s))
    
    # 8. 静态方法
    @staticmethod
    def utility():
        pass

4. 文档字符串

# ===== 模块文档 =====
"""
模块名称

模块的简要描述。

详细描述可以有多段。

Example:
    >>> import mymodule
    >>> mymodule.function()

Attributes:
    MODULE_CONSTANT: 模块级常量说明

Todo:
    * 待完成的功能
"""

# ===== 函数文档(Google风格) =====
def function_with_docstring(arg1: int, arg2: str, arg3: bool = True) -> dict:
    """函数的简要描述。

    更详细的描述可以有多行。

    Args:
        arg1: 第一个参数的描述。
        arg2: 第二个参数的描述。
        arg3: 第三个参数的描述,默认为True。

    Returns:
        返回值的描述。例如:
        {'key': 'value'}

    Raises:
        ValueError: 当arg1为负数时抛出。
        TypeError: 当arg2不是字符串时抛出。

    Example:
        >>> result = function_with_docstring(1, "test")
        >>> print(result)
        {'key': 'value'}
    """
    if arg1 < 0:
        raise ValueError("arg1不能为负数")
    return {'key': 'value'}

# ===== 类文档 =====
class DocumentedClass:
    """类的简要描述。

    更详细的描述。

    Attributes:
        attr1: 属性1的描述。
        attr2: 属性2的描述。

    Example:
        >>> obj = DocumentedClass("value")
        >>> obj.method()
    """

    def __init__(self, value: str):
        """初始化DocumentedClass。

        Args:
            value: 初始值。
        """
        self.attr1 = value
        self.attr2 = None

    def method(self, param: int) -> str:
        """方法的简要描述。

        Args:
            param: 参数描述。

        Returns:
            返回值描述。
        """
        return str(param)

# ===== NumPy风格文档 =====
def numpy_style_function(param1, param2):
    """
    函数简要描述。

    详细描述。

    Parameters
    ----------
    param1 : int
        参数1描述。
    param2 : str
        参数2描述。

    Returns
    -------
    bool
        返回值描述。

    See Also
    --------
    other_function : 相关函数。

    Examples
    --------
    >>> numpy_style_function(1, "test")
    True
    """
    return True

5. 类型提示

from typing import (
    List, Dict, Tuple, Set, Optional, Union,
    Callable, Iterator, Generator, Any,
    TypeVar, Generic
)

# ===== 基本类型提示 =====
def greet(name: str) -> str:
    return f"Hello, {name}"

def add(a: int, b: int) -> int:
    return a + b

# ===== 容器类型 =====
def process_list(items: List[int]) -> List[int]:
    return [x * 2 for x in items]

def get_user(users: Dict[str, int]) -> Optional[int]:
    return users.get("admin")

def get_point() -> Tuple[int, int]:
    return (0, 0)

# Python 3.9+ 可以直接用内置类型
def process_list_new(items: list[int]) -> list[int]:
    return [x * 2 for x in items]

# ===== Optional和Union =====
def find_user(user_id: int) -> Optional[str]:
    """返回用户名或None"""
    return None

def process(value: Union[int, str]) -> str:
    """接受int或str"""
    return str(value)

# Python 3.10+ 可以用 |
def process_new(value: int | str) -> str:
    return str(value)

# ===== Callable =====
def apply_func(func: Callable[[int, int], int], a: int, b: int) -> int:
    return func(a, b)

# ===== 泛型 =====
T = TypeVar('T')

def first(items: List[T]) -> T:
    return items[0]

class Stack(Generic[T]):
    def __init__(self):
        self.items: List[T] = []
    
    def push(self, item: T) -> None:
        self.items.append(item)
    
    def pop(self) -> T:
        return self.items.pop()

# ===== 类型别名 =====
UserId = int
UserDict = Dict[UserId, str]

def get_users() -> UserDict:
    return {1: "Alice", 2: "Bob"}

# ===== 类方法类型 =====
from typing import Self  # Python 3.11+

class Builder:
    def set_name(self, name: str) -> Self:
        self.name = name
        return self

# ===== dataclass与类型 =====
from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int
    email: Optional[str] = None

6. 代码检查工具

# ===== 安装工具 =====
pip install flake8 black isort mypy pylint

# ===== flake8:代码风格检查 =====
flake8 myfile.py
flake8 --max-line-length=100 myfile.py

# .flake8 配置文件
# [flake8]
# max-line-length = 100
# ignore = E501,W503

# ===== black:代码格式化 =====
black myfile.py
black --line-length 100 myfile.py
black --check myfile.py  # 只检查不修改

# ===== isort:导入排序 =====
isort myfile.py
isort --check-only myfile.py

# ===== mypy:类型检查 =====
mypy myfile.py
mypy --strict myfile.py

# mypy.ini 配置
# [mypy]
# python_version = 3.10
# warn_return_any = True
# warn_unused_ignores = True

# ===== pylint:综合检查 =====
pylint myfile.py

# ===== pre-commit:Git提交前检查 =====
# .pre-commit-config.yaml
# repos:
#   - repo: https://github.com/psf/black
#     rev: 23.1.0
#     hooks:
#       - id: black
#   - repo: https://github.com/pycqa/isort
#     rev: 5.12.0
#     hooks:
#       - id: isort
#   - repo: https://github.com/pycqa/flake8
#     rev: 6.0.0
#     hooks:
#       - id: flake8
# VS Code settings.json 配置
"""
{
    "python.linting.enabled": true,
    "python.linting.flake8Enabled": true,
    "python.formatting.provider": "black",
    "editor.formatOnSave": true,
    "[python]": {
        "editor.defaultFormatter": "ms-python.black-formatter",
        "editor.codeActionsOnSave": {
            "source.organizeImports": true
        }
    }
}
"""

7. 常见反模式

# ===== 反模式1:过长的函数 =====
# 不好
def do_everything(data):
    # 100行代码...
    pass

# 好:拆分成小函数
def process_data(data):
    validated = validate(data)
    transformed = transform(validated)
    return save(transformed)

# ===== 反模式2:魔法数字 =====
# 不好
if status == 1:
    pass

# 好:使用常量或枚举
from enum import Enum

class Status(Enum):
    ACTIVE = 1
    INACTIVE = 2

if status == Status.ACTIVE:
    pass

# ===== 反模式3:过深的嵌套 =====
# 不好
def process(data):
    if data:
        if data.valid:
            if data.ready:
                return data.value

# 好:提前返回
def process(data):
    if not data:
        return None
    if not data.valid:
        return None
    if not data.ready:
        return None
    return data.value

# ===== 反模式4:重复代码 =====
# 不好
def process_user(user):
    print(f"Processing {user.name}")
    # 处理逻辑

def process_admin(admin):
    print(f"Processing {admin.name}")
    # 相同的处理逻辑

# 好:抽取公共逻辑
def process_person(person):
    print(f"Processing {person.name}")
    # 处理逻辑

# ===== 反模式5:过度使用全局变量 =====
# 不好
config = {}

def init():
    global config
    config = load_config()

# 好:使用类或依赖注入
class App:
    def __init__(self, config):
        self.config = config

# ===== 反模式6:捕获所有异常 =====
# 不好
try:
    do_something()
except:
    pass

# 好:捕获特定异常
try:
    do_something()
except ValueError as e:
    logger.error(f"值错误: {e}")
except IOError as e:
    logger.error(f"IO错误: {e}")

# ===== 反模式7:使用可变默认参数 =====
# 不好
def append(item, lst=[]):
    lst.append(item)
    return lst

# 好
def append(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

8. Pythonic写法

# ===== 列表推导式 =====
# 不好
squares = []
for x in range(10):
    squares.append(x ** 2)

# 好
squares = [x ** 2 for x in range(10)]

# ===== 字典推导式 =====
# 不好
d = {}
for k, v in items:
    d[k] = v

# 好
d = {k: v for k, v in items}

# ===== 解包 =====
# 不好
first = lst[0]
rest = lst[1:]

# 好
first, *rest = lst

# ===== 交换变量 =====
# 不好
temp = a
a = b
b = temp

# 好
a, b = b, a

# ===== 条件表达式 =====
# 不好
if condition:
    x = 1
else:
    x = 2

# 好
x = 1 if condition else 2

# ===== enumerate =====
# 不好
for i in range(len(lst)):
    print(i, lst[i])

# 好
for i, item in enumerate(lst):
    print(i, item)

# ===== zip =====
# 不好
for i in range(len(names)):
    print(names[i], ages[i])

# 好
for name, age in zip(names, ages):
    print(name, age)

# ===== 上下文管理器 =====
# 不好
f = open('file.txt')
try:
    content = f.read()
finally:
    f.close()

# 好
with open('file.txt') as f:
    content = f.read()

# ===== 字符串连接 =====
# 不好
s = ""
for item in items:
    s += str(item)

# 好
s = "".join(str(item) for item in items)

# ===== 检查空集合 =====
# 不好
if len(lst) == 0:
    pass

# 好
if not lst:
    pass

# ===== 字典get =====
# 不好
if key in d:
    value = d[key]
else:
    value = default

# 好
value = d.get(key, default)

# ===== any/all =====
# 不好
found = False
for item in items:
    if condition(item):
        found = True
        break

# 好
found = any(condition(item) for item in items)

9. 项目结构

my_project/
├── README.md               # 项目说明
├── LICENSE                 # 许可证
├── pyproject.toml          # 项目配置(推荐)
├── setup.py                # 安装脚本(可选)
├── requirements.txt        # 依赖列表
├── requirements-dev.txt    # 开发依赖
├── .gitignore             # Git忽略文件
├── .env.example           # 环境变量示例

├── src/                   # 源代码
│   └── my_package/
│       ├── __init__.py
│       ├── main.py
│       ├── config.py
│       ├── utils/
│       │   ├── __init__.py
│       │   └── helpers.py
│       └── models/
│           ├── __init__.py
│           └── user.py

├── tests/                 # 测试代码
│   ├── __init__.py
│   ├── conftest.py
│   ├── test_main.py
│   └── test_utils.py

├── docs/                  # 文档
│   ├── index.md
│   └── api.md

├── scripts/               # 脚本
│   └── setup_db.py

└── data/                  # 数据文件
    └── sample.csv
# pyproject.toml 示例
[project]
name = "my_package"
version = "0.1.0"
description = "项目描述"
readme = "README.md"
requires-python = ">=3.9"
dependencies = [
    "requests>=2.28.0",
    "pandas>=1.5.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.0.0",
    "black>=23.0.0",
    "flake8>=6.0.0",
    "mypy>=1.0.0",
]

[tool.black]
line-length = 100

[tool.isort]
profile = "black"
line_length = 100

[tool.mypy]
python_version = "3.10"
warn_return_any = true

10. 总结

🔑 核心要点

规范要点
PEP 84空格缩进,79字符行宽
命名函数小写下划线,类大驼峰
文档使用docstring,Google或NumPy风格
类型提示使用typing模块,提高可读性
工具black格式化,flake8检查,mypy类型检查
Pythonic列表推导式,上下文管理器,解包

✅ 学习检查清单

  • 了解PEP 8基本规范
  • 掌握命名规范
  • 会写文档字符串
  • 了解类型提示
  • 会使用代码检查工具

📖 下一步学习

掌握了代码规范后,让我们学习常用第三方库:


常见问题 FAQ

💬 black和autopep8怎么选?

black是”无争议”格式化器,不给你选择,团队统一用black最省心。autopep8更温和,只修复PEP 8违规。推荐用black+isort组合。

💬 类型提示是必须的吗?

不是强制的,Python运行时不检查类型。但强烈推荐:配合Pylance/mypy可以在编辑时发现类型错误,相当于免费的静态分析。公共API和函数签名至少加上类型提示。


系列导航

End of file.