在 Python 中,生成器是用函数实现的迭代器:函数体包含 yield 时,调用该函数得到生成器对象而非立即执行完毕。每次 next 运行至下一个 yield,交出值并暂停,局部状态保留,下次从暂停处继续。生成器适合按需产生大量或无限序列,而不必一次性装入列表。本节说明 yield 语义及与列表推导的对比。

含 yield 的函数

调用 my_gen() 时函数体不会一次跑完,仅返回生成器;实际执行发生在 next 时:

def my_gen():
    yield 1
    yield 2
    yield 3

g = my_gen()
print(next(g))
print(next(g))
print(next(g))
# print(next(g))  # StopIteration

yield 暂停与恢复

生成器:执行 → 暂停 → 继续 gen() 调用 返回生成器 函数体没执行 第一次 next() 执行到 yield "A" 暂停,返回 A 第二次 next() 从暂停处继续 到 yield "B" 再停 第三次 next() 没有 yield 抛 StopIteration
遇到 yield 时函数暂停并返回一个值,保留现场;下次 next() 从暂停处继续,直至无 yield 可停则抛出 StopIteration。
def gen():
    print("开始")
    yield "A"
    print("继续")
    yield "B"

g = gen()
print(next(g))
print(next(g))

第一次 next 打印开始并返回 A;第二次从 yield "A" 之后继续,打印继续并返回 B。

无限序列

无法用有限列表表示的递增偶数序列,可用生成器边计算边产出:

def evens():
    n = 0
    while True:
        yield n
        n += 2

e = evens()
print(next(e))
print(next(e))
print(next(e))

列表推导与生成器表达式

方括号列表推导会立即构造完整列表;圆括号生成器表达式为惰性。生成器用尽后不能再次遍历:

squares_list = [x * x for x in range(10000000)]
squares_gen = (x * x for x in range(10000000))

print(type(squares_list))
print(type(squares_gen))
print(sum(squares_gen))

用 for 遍历生成器

def count_down(n):
    while n > 0:
        yield n
        n -= 1

for x in count_down(3):
    print(x, end=" ")
print()

按行读取大文件

在生成器内对文件 for line in f,可将各行依次交给外层循环,避免 read() 整文件进内存:

def read_lines(path):
    with open(path, "r", encoding="utf-8") as f:
        for line in f:
            yield line.strip()

for line in read_lines("demo.txt"):
    print(line)

生成器与手写迭代器类

维度生成器自定义迭代器类
写法函数 + yield类 + __iter__ / __next__
代码量通常更短样板较多
适用状态推进较直观需要完整类接口时

常见错误

  • 生成器同样只能完整消费一次,用尽后须重新调用生成器函数。
  • 生成器函数中的 return 主要用于结束迭代,返回值不会作为 next 的元素(与 yield 不同)。
  • 函数体中若无 yield,调用结果为普通返回值,不是生成器。

本节说明了 Python 中生成器与 yield 的行为。下一节介绍装饰器:@ 语法及 functools.wraps。