Python生成器generator之next和send运行流程

本文详细解释了Python中生成器的工作原理,包括next()和send()方法的区别与应用场景,并通过实例展示了如何利用生成器实现简单的生产者消费者模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


对于普通的生成器,第一个next调用,相当于启动生成器,会从生成器函数的第一行代码开始执行,直到第一次执行完yield语句(第4行)后,跳出生成器函数。

然后第二个next调用,进入生成器函数后,从yield语句的下一句语句(第5行)开始执行,然后重新运行到yield语句,执行后,跳出生成器函数,

后面再次调用next,依次类推。下面是一个列子:

1 def consumer():
2     r = 'here'
3     for i in xrange(3):
4         yield r
5         r = '200 OK'+ str(i)
6
7 c = consumer()
8 n1 = c.next()
9 n2 = c.next()
10 n3 = c.next()

了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做c.next() 和 c.send(None) 作用是一样的。需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值。
下面来着重说明下send执行的顺序。当第一次send(None)(对应11行)时,启动生成器,从生成器函数的第一行代码开始执行,直到第一次执行完yield(对应第4行)后,跳出生成器函数。这个过程中,n1一直没有定义。

下面运行到send(1)时,进入生成器函数,注意这里与调用next的不同。这里是从第4行开始执行,把1赋值给n1,但是并不执行yield部分。下面继续从yield的下一语句继续执行,然后重新运行到yield语句,执行后,跳出生成器函数。

即send和next相比,只是开始多了一次赋值的动作,其他运行流程是相同的。

1 def consumer():
2     r = 'here'
3     while True:
4         n1 = yield r
5         if not n1:
6             return
7         print('[CONSUMER] Consuming %s...' % n1)
8         r = '200 OK'+str(n1)
9
10 def produce(c):
11     aa = c.send(None)
12     n = 0
13     while n < 5:
14         n = n + 1
15         print('[PRODUCER] Producing %s...' % n)
16         r1 = c.send(n)
17         print('[PRODUCER] Consumer return: %s' % r1)
18     c.close()
19
20 c = consumer()
21 produce(c)

运行结果:

[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK1
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK2
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK3
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK4
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK5

<think>我们需要详细解释Python生成器next()send()方法的使用。生成器函数在调用时返回一个生成器对象,该对象支持迭代(通过__next__()方法)发送值(通过send()方法)的功能。1.next()方法:用于从生成器中获取下一个值。当生成器函数执行到yield表达式时,会暂停并返回yield后面的值。下次调用next()时,从上次暂停的位置继续执行,直到遇到下一个yield或函数结束(抛出StopIteration)。2.send()方法:与next()类似,但可以向生成器发送一个值。这个发送的值会成为当前暂停的yield表达式的值。注意:首次启动生成器时,必须使用next()或send(None)(因为此时还没有yield表达式来接收值)。接下来,我们用一个示例代码来演示这两种方法的使用。</think> ### Python生成器中的 `next()` 与 `send()` 方法详解 在 Python 生成器中,`next()` `send()` 是两个核心方法,用于控制生成器的执行流程: #### 1. `next()` 方法 - **功能**:启动生成器或从中获取下一个值 - **调用方式**: - 显式调用:`generator.next()` (Python 2) - 隐式调用:`next(generator)` (Python 3) 或在 `for` 循环中使用 - **行为**: - 首次调用:执行到第一个 `yield` 后暂停 - 后续调用:从暂停处继续执行到下一个 `yield` - 结束时:抛出 `StopIteration` 异常 #### 2. `send()` 方法 - **功能**:向暂停的生成器发送值并恢复执行 - **调用方式**:`generator.send(value)` - **行为**: - 发送的值会成为当前暂停处 `yield` 表达式的返回值 - 首次调用必须发送 `None`:`generator.send(None)` - 后续调用可发送任意值(包括 `None`) #### 代码示例:交互式生成器 ```python def interactive_counter(): total = 0 while True: # yield会暂停并等待外部输入 increment = yield total # yield total暂停,外部send的值赋值给increment if increment is None: increment = 1 # 默认行为:外部调用next() else: print(f"接收到增量值: {increment}") total += increment # 创建生成器对象 counter = interactive_counter() # 必须先用next()或send(None)启动生成器 print("初始启动:", next(counter)) # 输出: 0 # 发送值改变计数器行为 print("默认增量:", counter.send(None)) # 输出: 1(相当于next()) print("指定增量+5:", counter.send(5)) # 输出: 6 print("指定增量+3:", counter.send(3)) # 输出: 9 ``` #### 执行流程解析 1. **初始化状态**: ```python counter = interactive_counter() # 生成器创建,但尚未执行 ``` 2. **首次调用 `next()`**: - 执行到 `increment = yield total` 暂停 - 返回当前 `total(0)` - 生成器暂停等待外部交互 3. **后续调用 `send(None)`**: - `increment = None`(因为发送了None) - 执行 `increment = 1` - 执行循环下一次:`total += increment` → total=1 - 返回新值1 4. **发送特定值 `send(5)`**: - `increment = 5` - 打印接收消息 - `total += 5` → total=6 - 返回新值6 #### 高级应用:双向通信生成器 ```python def data_processor(): result = [] while True: data = yield result # 接收数据并返回结果 if data == "STOP": break # 处理数据(示例:大写转换) processed = data.upper() result.append(processed) processor = data_processor() next(processor) # 启动生成器 print("发送'hello':", processor.send("hello")) # 输出: ['HELLO'] print("发送'world':", processor.send("world")) # 输出: ['HELLO', 'WORLD'] print("结束处理:", processor.send("STOP")) # 引发StopIteration ``` #### 核心注意事项: 1. **首次启动规则**:必须使用 `next(generator)` 或 `generator.send(None)` 启动 2. **状态保存**:局部变量在多次调用间保持状态 3. **异常处理**:结束时抛出 `StopIteration` 4. **关闭生成器**:使用 `generator.close()` 显式终止 5. **替代方案**:协程 (`async/await`) 是更现代的替代方案
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值