Python yield 详解
Python yield
详解
yield
是 Python 中用于定义生成器函数的关键字。与返回整个结果列表的普通函数不同,生成器函数通过 yield
逐个产生值,允许你在迭代过程中逐步获取数据,而不是一次性将所有数据加载到内存中。这种方式对于处理大型数据集或创建无限序列非常有用,因为它可以显著减少内存占用并提高性能。
yield
的基本用法
一个简单的生成器函数示例如下:
def simple_generator():
yield 1
yield 2
yield 3
# 使用 for 循环遍历生成器
for value in simple_generator():
print(value)
上述代码会依次打印出 1
, 2
, 3
。每次遇到 yield
语句时,函数会暂停执行,并返回 yield
后面的值。当再次调用该函数(例如通过迭代)时,它会从上次暂停的地方继续执行,直到遇到下一个 yield
或者函数结束。
生成器对象
当你调用一个包含 yield
的函数时,实际上返回的是一个生成器对象,而不是立即执行函数体内的代码。你可以通过 next()
函数来手动获取生成器中的下一个值:
gen = simple_generator()
print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
print(next(gen)) # 输出: 3
# print(next(gen)) # 这行会引发 StopIteration 异常
一旦生成器的所有值都被消费完毕,再次调用 next()
将会抛出 StopIteration
异常,表示没有更多的值可以产生了。
避免 StopIteration
异常
为了避免 StopIteration
异常,可以在调用 next()
时提供一个默认值,或者使用 try-except
块来捕获异常。以下是两种常见的做法:
提供默认值
如果你不希望程序在生成器耗尽时抛出异常,可以为 next()
提供一个默认值。如果生成器没有更多值可返回,next()
将返回这个默认值,而不是抛出异常。
gen = simple_generator()
print(next(gen, None)) # 输出: 1
print(next(gen, None)) # 输出: 2
print(next(gen, None)) # 输出: 3
print(next(gen, None)) # 输出: None
使用 try-except
捕获异常
另一种方法是使用 try-except
块来捕获 StopIteration
异常,从而优雅地处理生成器耗尽的情况。
gen = simple_generator()
while True:
try:
value = next(gen)
print(value)
except StopIteration:
print("Generator has been exhausted.")
break
这两种方法都可以有效避免 StopIteration
异常,选择哪种取决于你的具体需求和代码风格。
生成器表达式
类似于列表推导式,Python 也支持生成器表达式,它提供了一种简洁的方式来创建生成器。生成器表达式的语法与列表推导式相似,只是使用圆括号 ()
而不是方括号 []
:
gen_expr = (x * x for x in range(5))
for num in gen_expr:
print(num) # 依次输出: 0, 1, 4, 9, 16
生成器的优点
yield
与 return
的区别
return
用于从函数中返回一个值,并终止函数的执行。而 yield
只是暂停函数的执行,并保存当前状态,以便后续可以从中断处恢复。return
语句(Python 3.3+),它可以用来传递一个最终值给生成器的使用者,但这个值会被封装在一个 StopIteration
异常中。此外,return
也可以用来提前终止生成器,不再产生新的值。发送值给生成器
除了产生值,生成器还可以接收外部发送的值。这可以通过 send()
方法实现,它允许你向生成器传递数据,并将其作为 yield
表达式的值:
def echo():
while True:
received = yield
print(f"Received: {received}")
gen = echo()
next(gen) # 必须先启动生成器
gen.send("Hello") # 输出: Received: Hello
gen.send("World") # 输出: Received: World
注意,必须先调用 next()
来启动生成器,否则 send()
会抛出 TypeError
。
抛出异常和关闭生成器
你可以使用 throw()
方法向生成器抛出异常,或者使用 close()
方法显式地关闭生成器。一旦生成器被关闭,任何进一步的调用都会导致 StopIteration
或 GeneratorExit
异常:
def my_generator():
try:
yield 1
yield 2
yield 3
except GeneratorExit:
print("Generator was closed")
gen = my_generator()
print(next(gen)) # 输出: 1
gen.close() # 输出: Generator was closed
# next(gen) # 这行会引发 StopIteration 异常
总结
yield
是 Python 中一个强大且灵活的特性,它使得编写高效的、可维护的代码变得更加容易。通过理解 yield
的工作原理以及如何正确地使用生成器,你可以更好地处理复杂的数据流和资源密集型任务。

作者:软件架构师笔记