Python 生成器练习指南:惰性取值
生成器不只是新语法,它改变了执行方式:产生一个值,暂停,之后再继续。当完整列表浪费内存、无法构建,或者根本不需要时,这种模型特别有用。
从带 yield 的函数开始
生成器函数看起来和普通函数一样,直到执行到 yield。此时它返回一个值,并记住局部状态。下一次请求从下一行继续。想拿到全部值时用 for 循环,想一次取一个值时用 next。
def countdown(n):
while n > 0:
yield n
n -= 1
for value in countdown(3):
print(value)暂停与继续
生成器只能消费一次
生成器对象是迭代器。耗尽之后再请求值会抛出 StopIteration。需要再次遍历数据时,要创建新的生成器。这一点和可以反复遍历的列表不同。
def pair():
yield 1
yield 2
gen = pair()
print(list(gen))
print(list(gen))第二次遍历为空
用表达式写短流水线
转换逻辑简单时,生成器表达式很紧凑。圆括号就够了,不必定义具名函数。它仍然是惰性的,只有用 list 或 sum 包起来时才真正请求值。
squares = (n * n for n in range(4))
print(sum(squares))一行式惰性流水线
把多个阶段串起来
一个生成器可以消费另一个生成器。这样可以把读取、筛选和格式化分开,而不必构建中间列表。每个阶段保持短小;流水线变长时,给阶段起清楚的名字。
def evens(values):
for value in values:
if value % 2 == 0:
yield value
print(list(evens(range(6))))一次处理一个阶段
知道什么时候该用列表
生成器用惰性内存换取随机访问和可重复遍历。需要 len、下标、排序或多轮遍历时,就转成列表。选择数据结构要看下一步操作,而不是看哪种写法最短。
values = list(n for n in range(3))
print(len(values))
print(values[1])需要时再物化
你的练习清单
- 值可以按需产生时使用 yield。
- 只需要一个值时再调用 next。
- 第二次遍历要创建新的生成器。
- 用 list、sum 或循环消费生成器表达式。
- 需要下标或长度时转成列表。
常见问题
生成器创建时就会运行吗?
不会。调用生成器函数只创建对象;推进生成器时函数体才运行。
生成器可以是无限的吗?
可以。count 和 while True 的生成器都可能无限。用 islice、break 或其他停止条件加上边界。
生成器一定比列表快吗?
它可能更省内存,也避免构建中间数据,但速度取决于具体工作。应该测量,而不是假设。
练习生成器
12 道题,覆盖 yield、next、惰性表达式和生成器耗尽。
继续阅读: