Python生成器是用函数书写的迭代器:函数体里出现 yield 后,调用该函数得到的是生成器对象。每次 next 会运行到下一个 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

暂停与继续

生成器:执行 → 暂停 → 继续 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 主要用来结束迭代,返回值不会变成 yield 出去的元素(高级用法另论)。
  • 函数里若没有任何 yield,它就只是普通函数,调用结果也不是生成器。