yield 是 Python 中的一个关键字,用于定义生成器(generator)。 生成器是一种特殊的迭代器,它可以记住上次迭代的位置,并在下一次迭代时从上次停止的地方继续执行。 yield 的使用使得函数可以像生成器一样工作,而不仅仅是返回一个值。
工作机制
暂停和恢复
yield 的核心功能是暂停函数的执行,并返回一个值。当生成器的 __next__()
方法被调用时,函数从上次暂停的地方继续执行。
def my_generator():
print("Start")
yield 1
print("Middle")
yield 2
print("End")
gen = my_generator()
print(next(gen)) # 输出:Start, 1
print(next(gen)) # 输出:Middle, 2
print(next(gen)) # 输出:End
状态保存
生成器函数在每次调用 yield 时会保存当前的状态,包括局部变量、指令指针等。当再次调用时,函数会从上次暂停的地方继续执行。
def counter(start, end):
while start < end:
yield start
start += 1
for num in counter(1, 5):
print(num) # 输出:1, 2, 3, 4
各种用法
定义生成器
使用 yield 的函数称为生成器函数,调用生成器函数会返回一个生成器对象。
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
print(next(gen)) # 输出:1
print(next(gen)) # 输出:2
print(next(gen)) # 输出:3
生成器可以用于 for 循环,每次迭代都会调用生成器函数,直到没有更多的 yield。
for value in my_generator():
print(value) # 输出:1, 2, 3
生成器表达式
类似于列表推导式,生成器表达式可以用来创建生成器。
# 列表推导式:立即生成全部数据
list_comp = [x * 2 for x in range(5)] # [0, 2, 4, 6, 8]
# 生成器表达式:按需生成数据
gen_exp = (x * 2 for x in range(5))
print(list(gen_exp)) # 输出: [0, 2, 4, 6, 8]
yield from
yield from 用于从一个生成器中委托调用另一个生成器。
def sub_generator():
yield "Sub 1"
yield "Sub 2"
def main_generator():
yield "Main 1"
yield from sub_generator()
yield "Main 2"
for item in main_generator():
print(item) # 输出: Main 1 → Sub 1 → Sub 2 → Main 2
双向通信:send() 方法
通过 send(value) 向生成器发送数据,yield 返回接收到的值。
def interactive_generator():
value = yield "Ready to receive"
while True:
value = yield f"Received: {value}"
gen = interactive_generator()
print(next(gen)) # 输出: Ready to receive
print(gen.send("Hello")) # 输出: Received: Hello
print(gen.send(123)) # 输出: Received: 123
数据流处理
生成器可以用于处理大型数据流,避免一次性加载所有数据到内存中。
def read_large_file(file_path):
with open(file_path, 'r') as file:
for line in file:
yield line.strip()
for line in read_large_file('large_file.txt'):
print(line)
异步编程
虽然 yield 本身不是异步的,但它可以用于实现协程(coroutine)。
# Python 3.4 之前的协程(使用生成器)
def old_coroutine():
while True:
received = yield
print(f"Received: {received}")
coro = old_coroutine()
next(coro) # 启动协程
coro.send("Hello") # 输出: Received: Hello
coro.send(123) # 输出: Received: 123
状态机
状态机是一种行为模型,用于描述一个系统在不同状态之间的转换。生成器通过 yield 暂停和恢复执行的特性,可以非常自然地实现状态机的行为。
基本原理
- 状态表示:每个 yield 表示一个状态。
- 状态转换:每次调用 next() 或 send() 时,生成器从一个状态转换到另一个状态。
- 暂停和恢复:生成器在每个状态处暂停执行,并在下一次调用时从上次暂停的地方继续执行。
def state_machine():
state = "State A"
while True:
if state == "State A":
print("Current state: State A")
action = yield
state = "State B"
elif state == "State B":
print("Current state: State B")
action = yield
state = "State C"
elif state == "State C":
print("Current state: State C")
action = yield
state = "State A"
# 创建状态机
machine = state_machine()
# 启动状态机
next(machine) # 初始状态为 State A
# 触发状态转换
machine.send(None) # 从 State A 到 State B
machine.send(None) # 从 State B 到 State C
machine.send(None) # 从 State C 回到 State A
machine.send(None) # 再次从 State A 到 State B
上下文管理器:contextlib.contextmanager
利用 yield 实现资源自动管理(如文件操作)。
from contextlib import contextmanager
@contextmanager
def file_manager(filename, mode):
file = open(filename, mode)
try:
yield file
finally:
file.close()
with file_manager("test.txt", "w") as f:
f.write("Hello World")
异常处理与生成器关闭
通过 throw() 向生成器抛出异常,close() 终止生成器。
def error_generator():
try:
yield "Start"
yield "Continue"
except ValueError as e:
yield f"Error handled: {e}"
finally:
yield "Cleaning up"
gen = error_generator()
print(next(gen)) # 输出: Start
print(gen.throw(ValueError("Oops!"))) # 输出: Error handled: Oops!
print(next(gen)) # 输出: Cleaning up → 抛出 StopIteration
gen.close() # 终止生成器
生产者-消费者模型
生成器实现协程协作,生产者发送数据,消费者处理数据。
def producer():
data = 0
while data < 3:
data += 1
yield f"Produced: {data}"
def consumer(prod):
while True:
item = next(prod)
print(f"Consumed: {item}")
prod = producer()
consumer(prod)
# 输出:
# Consumed: Produced: 1
# Consumed: Produced: 2
# Consumed: Produced: 3
# 抛出 StopIteration
惰性求值与大数据处理
逐行读取大文件,避免内存溢出。
def read_large_file(file_path):
with open(file_path, "r") as f:
for line in f:
yield line.strip()
for line in read_large_file("large_log.txt"):
process_line(line) # 逐行处理,内存友好
无限序列生成
生成器实现无限斐波那契数列。
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
for _ in range(10):
print(next(fib)) # 输出: 0 → 1 → 1 → 2 → 3 → 5 → 8 → 13 → 21 → 34
← Previous postTypeScript 中的面向Interface接口编程
Next post →Python中的上下文管理