跳转至

04 - 代码规范

学习时间: 2-3小时 重要性: ⭐⭐⭐⭐ 让代码更专业


🎯 学习目标

  • 理解代码规范的重要性
  • 掌握PEP 8规范的核心内容
  • 学会使用代码格式化工具
  • 配置pre-commit钩子
  • 建立良好的代码风格习惯

1. 为什么需要代码规范?

代码规范的价值

Python
# ❌ 不规范的代码 - 难以阅读和维护
def calc(a,b,c):
    d=a+b
    if c!=0:
        return d/c
    else:
        return None

# ✅ 规范的代码 - 清晰易懂
def calculate_average(num1: float, num2: float, divisor: float) -> float | None:
    """
    计算两个数的平均值后除以divisor。

    Args:
        num1: 第一个数字
        num2: 第二个数字
        divisor: 除数

    Returns:
        计算结果,如果divisor为0则返回None
    """
    total = num1 + num2

    if divisor == 0:
        return None

    return total / divisor

好处: - ✅ 提高代码可读性 - ✅ 便于团队协作 - ✅ 减少bug - ✅ 方便代码审查 - ✅ 提升专业形象


2. PEP 8规范详解

2.1 缩进和空格

Python
# ✅ 使用4个空格缩进(不要用Tab)
def my_function():
    if True:
        print("正确缩进")

# ✅ 运算符两侧加空格
x = 1 + 2
y = (a + b) * (c - d)

# ✅ 逗号后加空格
my_list = [1, 2, 3, 4]
my_dict = {'key': 'value', 'key2': 'value2'}

# ✅ 函数参数默认值两侧不加空格
def greet(name, greeting='Hello'):
    pass

# ❌ 错误示例
def bad_function( ):
    x=1+2  # 运算符两侧没有空格
    my_list=[1,2,3]  # 逗号后没有空格

2.2 命名规范

Python
# 变量名 - 小写,下划线分隔
user_name = '张三'
max_value = 100
is_valid = True

# 常量 - 全大写,下划线分隔
MAX_SIZE = 100
PI = 3.14159
DEFAULT_TIMEOUT = 30

# 函数名 - 小写,下划线分隔
def calculate_total():
    pass

def get_user_info():
    pass

# 类名 - 驼峰命名法
class UserProfile:
    pass

class DataProcessor:
    pass

# 私有变量/函数 - 下划线前缀
_private_var = 10

def _internal_function():
    pass

# 强私有 - 双下划线前缀(会触发名称修饰)
class MyClass:
    def __init__(self):
        self.__very_private = 42

2.3 行长度和换行

Python
# ✅ 每行不超过79个字符(现在通常放宽到88或100)

# 长参数列表换行
def my_function(
    parameter_one,
    parameter_two,
    parameter_three,
    parameter_four
):
    pass

# 长表达式换行
result = (some_long_variable_name
          + another_long_variable_name
          - yet_another_variable)

# 使用括号隐式换行
my_list = [
    'item_one',
    'item_two',
    'item_three',
    'item_four',
]

# 字符串换行
long_string = (
    "这是一个很长的字符串,"
    "需要分成多行来写。"
)

2.4 空行

Python
# 顶级函数和类之间用2个空行
def function_one():
    pass

def function_two():
    pass

class MyClass:
    # 类内部方法之间用1个空行
    def method_one(self):
        pass

    def method_two(self):
        pass

# 函数内部逻辑分组可以用空行
def process_data():
    # 数据加载
    data = load_data()

    # 数据清洗
    cleaned_data = clean_data(data)

    # 数据分析
    results = analyze(cleaned_data)

    return results

2.5 导入规范

Python
# ✅ 导入顺序:标准库 -> 第三方库 -> 本地模块

# 标准库
import os
import sys
from datetime import datetime

# 第三方库
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

# 本地模块
from mypackage import utils
from mypackage.models import MyModel

# ✅ 避免使用通配符导入
# ❌ from module import *

# ✅ 避免循环导入
# 如果必须,使用局部导入

2.6 注释和文档字符串

Python
# 行注释 - # 后加1个空格
x = x + 1  # 补偿边界

# 文档字符串(Docstrings)
def calculate_area(length: float, width: float) -> float:
    """
    计算矩形的面积。

    这是一个更详细的描述,可以包含多行。
    解释函数的用途、参数、返回值等。

    Args:
        length: 矩形的长度
        width: 矩形的宽度

    Returns:
        矩形的面积

    Raises:
        ValueError: 如果length或width为负数

    Examples:
        >>> calculate_area(5, 3)
        15.0
    """
    if length < 0 or width < 0:
        raise ValueError("长度和宽度必须为非负数")

    return length * width

class Calculator:
    """
    一个简单的计算器类。

    支持基本的数学运算。

    Attributes:
        history: 存储计算历史
    """

    def __init__(self):
        """初始化计算器。"""
        self.history = []

3. 代码格式化工具

3.1 Black

Black是Python社区广泛使用的代码格式化工具,号称"不妥协的代码格式化工具"。

Bash
# 安装
pip install black

# 格式化单个文件
black my_script.py

# 格式化整个目录
black my_project/

# 检查格式(不修改文件)
black --check my_script.py

# 显示差异
black --diff my_script.py

# 使用特定行长度(默认88)
black --line-length 100 my_script.py

Black配置(pyproject.toml):

TOML
[tool.black]
line-length = 88
target-version = ['py311']
include = '\.pyi?$'
extend-exclude = '''
/(
  # directories
  \.eggs
  | \.git
  | \.hg
  | \.mypy_cache
  | \.tox
  | \.venv
  | build
  | dist
)/
'''

3.2 isort

isort用于自动排序和组织import语句。

Bash
# 安装
pip install isort

# 格式化文件
isort my_script.py

# 检查格式
isort --check-only my_script.py

# 显示差异
isort --diff my_script.py

isort配置(pyproject.toml):

TOML
[tool.isort]
profile = "black"
multi_line_output = 3
line_length = 88
known_first_party = ["mypackage"]

3.3 autopep8

autopep8是另一个PEP 8格式化工具。

Bash
# 安装
pip install autopep8

# 格式化文件
autopep8 --in-place my_script.py

# 只修复特定问题
autopep8 --select=E101,W191 --in-place my_script.py

# 递归格式化
autopep8 --in-place --recursive my_project/

4. 代码质量检查工具

4.1 flake8

flake8是一个轻量级的代码风格检查工具。

Bash
# 安装
pip install flake8

# 检查文件
flake8 my_script.py

# 检查目录
flake8 my_project/

# 忽略特定错误
flake8 --ignore=E501,W503 my_script.py

# 设置最大行长度
flake8 --max-line-length=100 my_script.py

flake8配置(setup.cfg):

INI
[flake8]
max-line-length = 88
extend-ignore = E203, W503
exclude =
    .git,
    __pycache__,
    build,
    dist,
    .venv

4.2 pylint

pylint是一个更全面的代码质量检查工具。

Bash
# 安装
pip install pylint

# 检查文件
pylint my_script.py

# 生成报告
pylint --reports=y my_project/

# 禁用特定检查
pylint --disable=C0103 my_script.py

pylint配置(.pylintrc):

INI
[MASTER]
max-line-length=88

disable=
    C0103,  # 命名规范
    R0903,  # 类方法太少
    R0913   # 参数太多

[FORMAT]
good-names=i,j,k,ex,Run,_,logger

4.3 mypy

mypy是Python的静态类型检查工具。

Bash
# 安装
pip install mypy

# 检查文件
mypy my_script.py

# 检查目录
mypy my_project/

# 严格模式
mypy --strict my_script.py

5. pre-commit配置

pre-commit可以在提交代码前自动运行检查和格式化。

5.1 安装和配置

Bash
# 安装
pip install pre-commit

# 在项目中初始化
pre-commit install

.pre-commit-config.yaml:

YAML
# 参考配置
repos:
  # Black - 代码格式化
  - repo: https://github.com/psf/black
    rev: 23.12.1
    hooks:
      - id: black
        language_version: python3.11

  # isort - import排序
  - repo: https://github.com/pycqa/isort
    rev: 5.13.2
    hooks:
      - id: isort

  # flake8 - 代码风格检查
  - repo: https://github.com/pycqa/flake8
    rev: 7.0.0
    hooks:
      - id: flake8
        additional_dependencies: [flake8-docstrings]

  # mypy - 类型检查
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.8.0
    hooks:
      - id: mypy
        additional_dependencies: [types-all]

  # 其他有用的钩子
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace      # 去除行尾空格
      - id: end-of-file-fixer        # 确保文件以换行符结束
      - id: check-yaml               # 检查YAML语法
      - id: check-added-large-files  # 检查大文件
        args: ['--maxkb=1000']

5.2 使用pre-commit

Bash
# 手动运行所有钩子
pre-commit run --all-files

# 运行特定钩子
pre-commit run black

# 跳过钩子提交(不推荐)
git commit -m "message" --no-verify

# 更新钩子版本
pre-commit autoupdate

6. 项目配置示例

6.1 完整的pyproject.toml

TOML
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "myproject"
version = "0.1.0"
description = "My awesome project"
readme = "README.md"
requires-python = ">=3.11"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
]
dependencies = [
    "numpy>=1.24",
    "pandas>=2.0",
]

[project.optional-dependencies]
dev = [
    "black>=23.0",
    "isort>=5.12",
    "flake8>=6.0",
    "mypy>=1.0",
    "pre-commit>=3.0",
    "pytest>=7.0",
]

[tool.black]
line-length = 88
target-version = ['py311']

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

[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"

6.2 VS Code配置

.vscode/settings.json:

JSON
{
    "python.formatting.provider": "black",
    "python.formatting.blackArgs": ["--line-length", "88"],
    "python.sortImports.args": ["--profile", "black"],
    "python.linting.enabled": true,
    "python.linting.flake8Enabled": true,
    "python.linting.mypyEnabled": true,
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
        "source.organizeImports": true
    },
    "files.trimTrailingWhitespace": true,
    "files.insertFinalNewline": true
}

7. 代码审查清单

提交代码前检查

  • 代码是否符合PEP 8规范
  • 是否有适当的文档字符串
  • 变量和函数命名是否清晰
  • 是否有未使用的导入
  • 是否有未使用的变量
  • 复杂逻辑是否有注释说明
  • 是否处理了异常情况
  • 测试是否通过

代码审查关注点

Python
# ✅ 好的代码示例
class DataProcessor:
    """处理数据的类。"""

    def __init__(self, config: dict) -> None:
        """
        初始化处理器。

        Args:
            config: 配置字典
        """
        self.config = config
        self.logger = logging.getLogger(__name__)

    def process(self, data: pd.DataFrame) -> pd.DataFrame:
        """
        处理数据。

        Args:
            data: 输入数据

        Returns:
            处理后的数据

        Raises:
            ValueError: 如果数据为空
        """
        if data.empty:
            raise ValueError("数据不能为空")

        try:
            result = self._transform(data)
            self.logger.info(f"处理完成: {len(result)} 条记录")
            return result
        except Exception as e:
            self.logger.error(f"处理失败: {e}")
            raise

📝 练习

练习1: 格式化现有代码

Python
# 将以下不规范的代码用Black格式化

def badcode(x,y,z):
    a=x+y
    if z>0:
        return a/z
    else:return None

练习2: 配置pre-commit

Bash
# 1. 在你的项目中安装pre-commit
# 2. 创建.pre-commit-config.yaml文件
# 3. 配置black、isort、flake8
# 4. 运行pre-commit install
# 5. 尝试提交代码,观察自动格式化效果

练习3: 代码审查

Python
# 找出以下代码中的所有规范问题

import os,sys
import numpy as np

def processData(data):
  x=data+1
  return x

class my_class:
    def __init__(self):
        self.x=1
    def do_something(self):
        print("doing something")

🎯 自我检查

  • 理解PEP 8的核心规范
  • 掌握命名规范(变量、函数、类、常量)
  • 会使用Black格式化代码
  • 会使用isort整理导入
  • 会使用flake8检查代码风格
  • 会配置pre-commit钩子
  • 会编写规范的文档字符串
  • 理解代码审查的重要性

📚 延伸阅读


下一步: 05 - 版本控制