Vanson's Eternal Blog

Python中的yield

Python yield.png
Published on
/7 mins read/---

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