问题: Python 中推导式和生成器表达式的核心区别是什么?
👇 点击展开/折叠 面试背诵模板(建议先背这个)
推导式是立即执行并返回完整容器(列表/字典/集合)的语法,生成器表达式返回生成器对象,惰性求值按需产出数据。推导式用方括号 `[]` 或花括号 `{}`,生成器用圆括号 `()`。生成器迭代一次即耗尽,且不支持索引和 `len()`,但内存占用极小;推导式适合数据量可控的中等规模场景,生成器表达式适合处理海量数据流或无限序列。
---
语法形态与返回类型速览
| 写法 |
返回类型 |
执行时机 |
内存行为 |
[x**2 for x in range(10)] |
列表(list) |
立即计算 |
一次性分配全部元素 |
{x: x**2 for x in range(10)} |
字典(dict) |
立即计算 |
一次性分配全部键值对 |
{x%3 for x in range(10)} |
集合(set) |
立即计算 |
一次性分配全部元素 |
(x**2 for x in range(10)) |
生成器(generator) |
惰性(迭代时计算) |
仅存当前状态,极省内存 |
执行时机与内存占用对比
1 2 3 4 5 6 7 8 9
| import sys
lst = [i**2 for i in range(1_000_000)] print(sys.getsizeof(lst))
gen = (i**2 for i in range(1_000_000)) print(sys.getsizeof(gen))
|
迭代特性与访问限制
生成器是一次性迭代器,遍历完后无法重置或重复使用,且不支持通过索引取值(gen[0] 报错),只能通过 next() 或 for 循环顺序访问。推导式返回的容器支持任意次遍历、索引切片和 len() 查询。
1 2 3 4 5 6 7
| gen = (x for x in range(3)) print(list(gen)) print(list(gen))
lst = [x for x in range(3)] print(lst[1]) print(lst)
|
性能权衡与选型速查
| 场景 |
推荐方案 |
理由 |
| 数据量小(< 万级)且需多次遍历 |
列表推导式 |
速度更快,重复访问友好 |
| 数据量极大(百万级、GB级文件流) |
生成器表达式 |
内存不爆炸,按需逐条处理 |
| 网络请求分页抓取(逐页返回) |
生成器表达式 |
无需等待全部数据到达 |
| 需随机访问或修改已有元素 |
列表推导式 |
生成器不支持索引和赋值 |
链式调用(如 sum()、any()) |
生成器表达式 |
函数内部遍历即可,省去中间容器 |
常见陷阱与最佳实践
- 生成器不保留数据:若后续代码需要多次使用同一批数据,应显式转为列表或重新创建生成器,否则第二次迭代为空。
- 嵌套生成器需逐层驱动:生成器内部包含生成器时,若不迭代内层,外层也不会计算(惰性会传递)。
- 生成器内存优势随操作变化:
sum((x for x in range(N))) 与 sum([x for x in range(N)]) 相比,前者不创建大列表,内存效率显著提升。