Python 函数文档字符串(docstring)编写规范与实战指南

你写完一个函数,过两周再看------参数是什么意思?返回什么类型?有没有副作用?完全想不起来。

好的 docstring 就是一份「随时可查的说明书」。这篇文章教你从零写出规范的 Python 文档字符串,不仅自己看得懂,团队协作和 IDE 提示也能直接用上。


一、docstring 是什么?

docstring 就是函数、类、模块第一行写的字符串,Python 会自动把它当作文档保存起来。

python 复制代码
def add(a, b):
    """返回两个数的和。"""
    return a + b


# 随时可以查看
print(add.__doc__)
# 输出:返回两个数的和。

# help() 也能直接看到
help(add)

看起来很简单,但写什么样的 docstring写多少细节用什么格式,这些才是真正拉开差距的地方。


二、docstring 的基本规则

2.1 位置要求

docstring 必须是函数/类/模块的第一条语句,紧跟在定义行之后:

python 复制代码
# ✅ 正确
def process(data):
    """处理输入数据。"""
    ...

# ❌ 错误:docstring 之前有其他代码
def process(data):
    x = 1               # ← 这行在 docstring 前面,不行
    """处理输入数据。"""
    ...

2.2 引号选择

三引号,单引号双引号都行,团队里保持一致即可:

python 复制代码
def func():
    """三双引号,最常用。"""
    pass

def func():
    '''三单引号,也行。'''
    pass

2.3 多行写法

一行放得下就一行,放不下就换行:

python 复制代码
# 单行:简短描述就行
def max_value(items):
    """返回列表中的最大值。"""
    pass


# 多行:第一行概述,空一行,写详细说明
def max_value(items):
    """
    返回列表中的最大值。

    如果列表为空,返回 None。

    Args:
        items: 数字列表。

    Returns:
        列表中的最大值,或 None。
    """
    pass

格式约定:单行 docstring 不加首尾空行,多行 docstring 第一行概述 + 空一行 + 详细内容。


三、四大主流 docstring 风格,选一种坚持用

Python 社区没有「官方强制标准」,但有几种主流风格。选哪种不重要,重要的是全项目统一

3.1 Google 风格(推荐)

python 复制代码
def calculate_area(width, height):
    """
    计算矩形的面积。

    Args:
        width (float): 矩形的宽度,必须大于 0。
        height (float): 矩形的高度,必须大于 0。

    Returns:
        float: 矩形的面积。

    Raises:
        ValueError: 当 width 或 height 小于等于 0 时抛出。

    Examples:
        >>> calculate_area(3, 4)
        12.0
    """
    if width <= 0 or height <= 0:
        raise ValueError("宽度和高度必须大于 0")
    return width * height

优点:可读性好,结构清晰,被 TensorFlow、PyTorch 等主流项目采用。

3.2 NumPy 风格

python 复制代码
def calculate_area(width, height):
    """
    计算矩形的面积。

    Parameters
    ----------
    width : float
        矩形的宽度,必须大于 0。
    height : float
        矩形的高度,必须大于 0。

    Returns
    -------
    float
        矩形的面积。

    Raises
    ------
    ValueError
        当 width 或 height 小于等于 0 时抛出。

    Examples
    --------
    >>> calculate_area(3, 4)
    12.0
    """
    if width <= 0 or height <= 0:
        raise ValueError("宽度和高度必须大于 0")
    return width * height

优点 :科学计算领域的主流风格,NumPy、SciPy、Pandas 都在用。--- 分隔线在纯文本下视觉更清晰。

3.3 reST(Sphinx)风格

python 复制代码
def calculate_area(width, height):
    """
    计算矩形的面积。

    :param width: 矩形的宽度,必须大于 0
    :type width: float
    :param height: 矩形的高度,必须大于 0
    :type height: float
    :returns: 矩形的面积
    :rtype: float
    :raises ValueError: 当 width 或 height 小于等于 0 时抛出
    """
    if width <= 0 or height <= 0:
        raise ValueError("宽度和高度必须大于 0")
    return width * height

优点:Sphinx 文档工具原生支持,适合需要生成 HTML/PDF 文档的大型项目。

3.4 风格对比速查表

对比项 Google NumPy reST
可读性 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
排版简洁度 ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐
适合场景 通用项目 科学计算 需 Sphinx 生成文档
代表项目 TensorFlow、PyTorch NumPy、Pandas Django(部分)

建议 :新手直接选 Google 风格,清晰易懂,覆盖面最广。


四、每个部分该怎么写

一个完整的 docstring 通常包含以下几个部分,按需选用,别全抄。

4.1 概述(必写)

第一行,一句话说明这个函数做什么,不是怎么做。

python 复制代码
# ✅ 好:说明做什么
def fetch_data(url):
    """从指定 URL 获取 JSON 数据并返回字典。"""
    pass

# ❌ 差:在说怎么做,不是做什么
def fetch_data(url):
    """使用 requests 库发送 GET 请求,解析 JSON 返回值。"""
    pass

4.2 Args / Parameters(有参数就写)

说明每个参数的含义和类型

python 复制代码
def search_users(name=None, age_min=0, role="member"):
    """
    搜索符合条件的用户。

    Args:
        name (str, optional): 用户名关键字,支持模糊匹配。默认为 None。
        age_min (int): 最小年龄,默认为 0。
        role (str): 用户角色,可选值为 member、admin、superadmin。
    """
    pass

要点

  • 写类型 + 含义 + 可选值/默认值
  • optional 标注有默认值的参数
  • 一个参数占一行

4.3 Returns(有返回值就写)

说明返回值的类型和含义

python 复制代码
def get_user_info(user_id):
    """
    获取用户信息。

    Args:
        user_id (int): 用户 ID。

    Returns:
        dict: 用户信息字典,包含以下字段:
            - name (str): 用户名
            - email (str): 邮箱
            - role (str): 角色
            如果用户不存在,返回 None。
    """
    pass

要点

  • 如果返回多种类型,用 Union[str, None] 或写清楚各条件
  • 返回复杂结构时,列出字段说明

4.4 Raises(有异常就写)

说明函数可能抛出的异常

python 复制代码
def divide(a, b):
    """
    计算两数之商。

    Args:
        a (float): 被除数。
        b (float): 除数,不能为 0。

    Returns:
        float: 两数之商。

    Raises:
        ZeroDivisionError: 当 b 为 0 时抛出。
        TypeError: 当 a 或 b 不是数字类型时抛出。
    """
    return a / b

要点

  • 只写函数主动抛出的异常,不写 Python 内部可能抛出的
  • ValueErrorTypeError 是最常见的两类

4.5 Examples(强烈建议写)

给一个可运行的示例,比一大段描述管用:

python 复制代码
def format_price(amount, currency="CNY"):
    """
    格式化价格显示。

    Args:
        amount (float): 金额。
        currency (str): 货币代码,默认为 CNY。

    Returns:
        str: 格式化后的价格字符串。

    Examples:
        >>> format_price(99.9)
        '¥99.90'
        >>> format_price(49.5, currency="USD")
        '$49.50'
    """
    pass

有 Examples 的 docstring,可以直接用 doctest 模块自动跑测试,一举两得。

4.6 Notes / See Also(按需写)

python 复制代码
def train_model(data, epochs=100):
    """
    训练文本分类模型。

    Args:
        data (pd.DataFrame): 训练数据。
        epochs (int): 训练轮数。

    Returns:
        Model: 训练好的模型对象。

    Note:
        使用预训练的 BERT-base 作为编码器。
        数据量少于 1000 条时建议减少 epochs。

    See Also:
        evaluate_model: 模型评估函数
        save_model: 模型保存函数
    """
    pass

五、不同场景的 docstring 示例

5.1 简单工具函数

python 复制代码
def is_palindrome(s):
    """
    判断字符串是否为回文(忽略大小写和空格)。

    Args:
        s (str): 待判断的字符串。

    Returns:
        bool: 是回文返回 True,否则返回 False。

    Examples:
        >>> is_palindrome("racecar")
        True
        >>> is_palindrome("hello")
        False
        >>> is_palindrome("A man a plan a canal Panama")
        True
    """
    cleaned = s.lower().replace(" ", "")
    return cleaned == cleaned[::-1]

5.2 类的 docstring

python 复制代码
class DataLoader:
    """
    数据加载与预处理工具类。

    支持从 CSV、JSON、Parquet 格式加载数据,
    并提供缺失值填充、类型转换等预处理功能。

    Attributes:
        data (pd.DataFrame): 加载后的数据。
        filepath (str): 数据文件路径。
        encoding (str): 文件编码格式。

    Examples:
        >>> loader = DataLoader("data.csv", encoding="gbk")
        >>> loader.load()
        >>> loader.info()
    """

    def __init__(self, filepath, encoding="utf-8"):
        """初始化数据加载器。

        Args:
            filepath (str): 数据文件路径。
            encoding (str): 文件编码,默认为 utf-8。
        """
        self.filepath = filepath
        self.encoding = encoding
        self.data = None

5.3 带类型注解的函数(推荐)

docstring + 类型注解双保险,IDE 提示最完整:

python 复制代码
from typing import Optional, List, Dict

def get_top_users(
    data: List[Dict[str, object]],
    n: int = 10,
    sort_by: str = "score",
    ascending: bool = False
) -> List[Dict[str, object]]:
    """
    获取排名前 N 的用户。

    Args:
        data: 用户数据列表,每个元素是包含用户信息的字典。
        n: 返回的用户数量,默认为 10。
        sort_by: 排序字段名,默认为 score。
        ascending: 是否升序排列,默认为 False(降序)。

    Returns:
        排序后的前 N 个用户数据列表。

    Raises:
        KeyError: 当 sort_by 指定的字段不存在时抛出。
        ValueError: 当 n 小于 1 时抛出。
    """
    pass

类型注解写在函数签名里,docstring 重点写业务含义,不要重复写类型。


六、模块级 docstring

每个 Python 文件顶部也应该写 docstring,说明这个模块是做什么的:

python 复制代码
"""
数据处理工具模块。

提供数据清洗、格式转换、缺失值处理等常用功能。
主要服务于用户行为分析项目。

模块依赖:
    - pandas >= 1.5
    - numpy >= 1.23

使用方式:
    from data_utils import clean_dataframe, parse_dates
    df = clean_dataframe(raw_data)
"""

七、docstring 和注释的区别

这是很多人搞混的点:

python 复制代码
def calculate_discount(price, level):
    """
    根据用户等级计算折扣价格。

    Args:
        price (float): 原价。
        level (str): 用户等级,可选 gold、silver、bronze。

    Returns:
        float: 折后价格。
    """
    # VIP 等级使用不同的折扣率(这是注释,解释实现逻辑)
    discount_map = {
        "gold": 0.7,     # 金牌会员 7 折
        "silver": 0.85,   # 银牌会员 85 折
        "bronze": 0.95,   # 铜牌会员 95 折
    }

    # 如果等级不存在,不打折(这是注释,说明边界情况)
    rate = discount_map.get(level, 1.0)

    return price * rate
对比 docstring 注释
写给谁看 使用这个函数的人 维护这段代码的人
写什么 做什么、参数、返回值 为什么这么写
位置 函数/类/模块的第一行 代码内部的任意位置
能否被 help() 查看 ✅ 可以 ❌ 不行

一句话:docstring 写「接口说明」,注释写「实现逻辑」


八、自动化工具:让规范不再靠自觉

光靠人肉规范不靠谱,用工具自动检查和生成。

8.1 pydoc:Python 自带文档生成

bash 复制代码
# 命令行查看文档
pydoc your_module

# 生成 HTML 文档
pydoc -w your_module

8.2 Sphinx:专业文档生成工具

bash 复制代码
pip install sphinx

# 在项目目录下初始化
sphinx-quickstart

# 自动从 docstring 生成 API 文档
sphinx-apidoc -o docs/ src/

NumPy、Pandas、Matplotlib 的官方文档都是 Sphinx 生成的。

8.3 docformatter:自动格式化 docstring

bash 复制代码
pip install docformatter

# 自动格式化
docformatter --in-place your_file.py

8.4 pydocstyle:检查 docstring 规范

bash 复制代码
pip install pydocstyle

# 检查所有 Python 文件
pydocstyle src/

# 检查单个文件
pydocstyle utils.py

推荐 :把 pydocstyle 加入 pre-commit hook 或 CI 流水线,不规范直接报错,逼着团队遵守规范。


九、写好 docstring 的 5 个原则

  1. 说做什么,不说怎么做------docstring 是给调用者看的,不是给自己的代码笔记
  2. 有参数就写参数,有返回就写返回------别偷懒,这些是最常用的信息
  3. 给 Examples------一段可运行的示例比三段描述管用,还能当测试用
  4. 和类型注解配合------类型交给注解,业务含义交给 docstring,别重复
  5. 风格统一------一个项目只选一种风格,别这个函数 Google 风格那个函数 NumPy 风格

写 docstring 不是浪费时间,是在给未来的自己和队友省时间。养成习惯,你会感谢现在写了 docstring 的自己。