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

    • 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章 工程实践

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

生成器表达式

生成器表达式是一种紧凑的语法,用于创建简单的生成器。它的写法类似列表推导式,但外层使用圆括号而非方括号,产出的结果是一个惰性计算的迭代器,而非一次性构建的列表。

基本语法

生成器表达式的语法为 (expression for item in iterable),与列表推导式 [expression for item in iterable] 的区别仅在于括号类型。

# 列表推导式:一次性计算所有元素,存入列表
squares_list = [x * x for x in range(10)]
print(type(squares_list))  # <class 'list'>
print(squares_list)        # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 生成器表达式:惰性计算,按需产出
squares_gen = (x * x for x in range(10))
print(type(squares_gen))   # <class 'generator'>
print(next(squares_gen))   # 0
print(next(squares_gen))   # 1

生成器表达式返回的是一个生成器对象,它不会立即执行循环体,而是等到被迭代时才逐个计算元素。

与列表推导式的核心区别

特性列表推导式生成器表达式
语法[...](...)
返回值listgenerator
内存占用O(n),存储所有元素O(1),只保存状态
计算时机立即执行惰性求值,按需计算
可复用性可多次遍历一次性,耗尽后不可复用
索引访问支持 lst[i]不支持
长度查询支持 len(lst)不支持
# 内存对比:处理百万级数据
import sys

# 列表推导式占用大量内存
big_list = [x for x in range(1_000_000)]
print(sys.getsizeof(big_list))  # 约 8MB

# 生成器表达式几乎不占内存
big_gen = (x for x in range(1_000_000))
print(sys.getsizeof(big_gen))   # 约 112 字节

生成器表达式的内存效率来自它的惰性特性:它只保存当前迭代状态(当前索引、循环变量等),而不是把所有结果存储在内存中。这使得处理大规模数据流成为可能。

惰性求值的实际应用

生成器表达式最适合"消费后立即丢弃"的场景,即元素被计算出来后马上用于某种聚合操作,不需要保留。

# 计算 0 到 9 的平方和
# 不需要存储所有平方数,只需逐个累加
total = sum(x * x for x in range(10))
print(total)  # 285

# 计算两个向量的点积
xvec = [10, 20, 30]
yvec = [7, 5, 3]
dot_product = sum(x * y for x, y in zip(xvec, yvec))
print(dot_product)  # 260

# 查找最大值,基于对象属性
students = [
    {"name": "Alice", "gpa": 3.8},
    {"name": "Bob", "gpa": 3.5},
    {"name": "Charlie", "gpa": 3.9},
]
valedictorian = max((s["gpa"], s["name"]) for s in students)
print(valedictorian)  # (3.9, 'Charlie')

在这些例子中,生成器表达式作为参数直接传递给 sum()、max() 等函数,无需中间列表。如果写成列表推导式,会先创建一个完整列表再传给函数,浪费内存。

多循环与条件过滤

生成器表达式支持多重 for 子句和 if 条件,与列表推导式的语法完全一致。

# 嵌套循环:从二维结构中提取单词
page = ["hello world", "python tutorial"]
unique_words = set(word for line in page for word in line.split())
print(unique_words)  # {'hello', 'world', 'python', 'tutorial'}

# 带条件过滤:只处理正数的平方根
import math
data = [4, -1, 9, 0, -4, 16]
roots = (math.sqrt(x) for x in data if x > 0)
print(list(roots))  # [2.0, 3.0, 4.0]

# 复杂条件:筛选成绩合格的学生
scores = [("Alice", 85), ("Bob", 59), ("Charlie", 72)]
passed = (name for name, score in scores if score >= 60)
for name in passed:
    print(f"{name} 通过考试")

多重 for 子句的执行顺序与嵌套循环相同:外层循环先固定,内层循环遍历全部元素,然后外层前进一步。条件 if 在内层循环体执行前判断,只有满足条件的元素才会被产出。

圆括号的省略规则

当生成器表达式是函数的唯一参数时,可以省略外层的圆括号,只保留用于函数调用的括号。

# 完整写法
sum((x * x for x in range(10)))

# 省略生成器表达式的圆括号(推荐)
sum(x * x for x in range(10))

# 多个参数时不能省略
print(list(x * x for x in range(10)))  # 可以,list() 是唯一参数
# print(sum(x for x in range(10)), "extra")  # 不行,sum 不是唯一参数

这种省略让代码更简洁,但只在生成器表达式作为唯一参数时有效。如果有多个参数,必须显式写出圆括号。

边界情况与注意事项

  • 一次性消费:生成器表达式和生成器函数一样,只能遍历一次。如果需要多次使用,要么转换为列表,要么重新创建生成器表达式。
gen = (x for x in range(3))
print(list(gen))  # [0, 1, 2]
print(list(gen))  # [],已耗尽
  • 惰性副作用:由于惰性求值,生成器表达式中的副作用(如打印、修改全局状态)不会在定义时执行,而是在遍历时才触发。
gen = (print(f"计算 {x}") or x * x for x in range(3))
print("生成器已创建,但尚未计算")
for val in gen:
    print(f"得到 {val}")
  • 不支持索引和长度:生成器表达式返回的是迭代器,不支持 len() 和索引访问。如果需要随机访问,应使用列表推导式或 list() 转换。
gen = (x for x in range(10))
# print(len(gen))     # TypeError
# print(gen[5])       # TypeError
print(list(gen)[5])   # 先转列表,再索引
  • 与字典/集合推导式区分:字典推导式用 {k: v for ...},集合推导式用 {x for ...},它们都是立即求值的。只有圆括号 (...) 才表示生成器表达式。
# 集合推导式(立即求值,返回 set)
unique = {x % 3 for x in range(10)}
print(type(unique))  # <class 'set'>

# 生成器表达式(惰性求值,返回 generator)
unique_gen = (x % 3 for x in range(10))
print(type(unique_gen))  # <class 'generator'>
上一页
生成器
下一页
迭代工具