飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

文档字符串与注解

好的函数不仅需要正确实现功能,还需要清晰的接口说明。Python 提供了两种元数据机制:文档字符串(docstring)描述函数的用途和用法,函数注解(annotation)标注参数和返回值的预期类型。两者都是可选的,但合理使用能显著提升代码的可维护性。

文档字符串规范

函数体内的第一个语句如果是字符串字面量,它就会被识别为文档字符串,存储在函数对象的 __doc__ 属性中:

def greet(name):
    """向指定用户问好。

    参数:
        name (str): 用户的名字。

    返回:
        str: 问候语。
    """
    return f"你好,{name}!"

print(greet.__doc__)

PEP 257 对文档字符串的格式有明确约定:

  1. 第一行是简短摘要,说明对象用途。不要在这里写对象名或类型(可通过其他方式获取),以大写字母开头,以句点结尾。
  2. 多行文档字符串的第二行应为空行,在视觉上将摘要与详细描述分开。
  3. 后续行描述调用约定、参数、返回值、副作用、异常等。
def calculate_area(length, width):
    """计算矩形的面积。

    根据给定的长和宽计算矩形面积。如果输入为负数,
    返回 0 而不是抛出异常。

    参数:
        length (float): 矩形的长度,必须为非负数。
        width (float): 矩形的宽度,必须为非负数。

    返回:
        float: 矩形的面积。如果输入为负数则返回 0。
    """
    if length < 0 or width < 0:
        return 0
    return length * width

doc 属性的访问

文档字符串可以通过 __doc__ 属性或 help() 函数查看:

print(greet.__doc__)    # 直接打印原始字符串
help(greet)             # 格式化输出,包含签名和文档字符串

在交互式解释器和 IDE 中,help() 是快速查阅函数说明的标准方式。第三方工具如 Sphinx 可以从文档字符串自动生成 HTML 文档。

缩进处理

Python 解析器不会删除多行字符串字面量中的缩进,因此文档字符串中保留了函数体内的缩进空格。文档处理工具(如 help())会按约定自动去除缩进:以文档字符串第一行之后的第一个非空行确定基准缩进量,然后删除所有行开头处等价的空白。

def example():
    """第一行摘要。

    这是详细描述。
    缩进与函数体一致。
    """
    pass

print(example.__doc__)
# 第一行摘要。
#
#     这是详细描述。
#     缩进与函数体一致。

注意第一行通常与引号相邻,其缩进在字符串中不明显,因此不能用第一行确定缩进量。

函数注解语法

函数注解是可选的元数据,以字典形式存放在函数的 __annotations__ 属性中。注解对函数执行没有任何影响,Python 解释器不会检查或强制类型。

参数注解写在形参名后加冒号:

def greet(name: str, times: int = 1) -> str:
    return (f"你好,{name}!\n") * times

print(greet.__annotations__)
# {'name': <class 'str'>, 'times': <class 'int'>, 'return': <class 'str'>}

返回值注解写在参数列表和冒号之间,用 -> 表示:

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

注解不强制检查

注解只是提示,Python 运行时完全忽略它们。传入"错误"类型的数据不会报错:

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

print(add("hello, ", "world"))   # "hello, world",完全合法
print(add(1.5, 2.5))            # 4.0,也合法

如果需要类型检查,应使用外部工具如 mypy、Pyright 或 IDE 的静态分析功能。这些工具在开发时检查类型一致性,而不影响运行时性能。

复杂注解

注解可以是任何表达式,不限于内置类型。从 Python 3.9 开始,内置集合类型(list、dict、set 等)支持泛型语法:

def process(items: list[int]) -> dict[str, int]:
    return {str(x): x for x in items}

print(process([1, 2, 3]))
# {'1': 1, '2': 2, '3': 3}

对于可选值、联合类型等复杂场景,可以使用 typing 模块或 | 语法(Python 3.10+):

def find(items: list[str], target: str) -> int | None:
    try:
        return items.index(target)
    except ValueError:
        return None

print(find(["a", "b", "c"], "b"))   # 1
print(find(["a", "b", "c"], "z"))   # None

Python 3.12 泛型语法(PEP 695)

Python 3.12 引入了更简洁的泛型函数语法,用 def[T] 声明类型参数:

def get_first[T](items: list[T]) -> T | None:
    return items[0] if items else None

print(get_first([1, 2, 3]))       # 1
print(get_first(["a", "b"]))      # "a"

旧写法(3.11 及之前)需要显式导入 TypeVar:

from typing import TypeVar, List, Optional

T = TypeVar('T')

def get_first(items: List[T]) -> Optional[T]:
    return items[0] if items else None

新写法减少了样板代码,但只在 Python 3.12+ 可用。

常见错误

把注解当作运行时类型检查:

# 错误认知:以为注解会阻止错误类型传入
# 实际上 Python 完全忽略注解

注解表达式在定义时求值,如果引用了尚未定义的名称会报错:

# def broken(x: MyClass) -> MyClass:   # NameError: name 'MyClass' is not defined
#     pass

# 解决方案:用字符串延迟求值(前向引用)
def fixed(x: "MyClass") -> "MyClass":
    pass

class MyClass:
    pass

从 Python 3.7+ 开始,可以通过 from __future__ import annotations 让所有注解自动变为字符串形式,解决前向引用问题。

小结

文档字符串通过 __doc__ 提供运行时可查的说明,遵循 PEP 257 的格式约定。函数注解通过 __annotations__ 提供类型元数据,但 Python 不强制检查,需配合静态分析工具使用。两者都是可选的,但在团队协作和大型项目中,它们是代码自文档化的重要手段。

上一页
lambda 表达式
下一页
作用域与命名空间