首页 文章

Python如何处理对生成器的多个“发送”调用?

提问于
浏览
1

有关类似问题的一些好问题,例如:

python generator "send" function purpose?

What does the "yield" keyword do?

让我们回到“发送”的定义:

恢复执行并将值“发送”到生成器函数中 . value参数成为当前yield表达式的结果 . send()方法返回生成器产生的下一个值,如果生成器退出而不产生另一个值,则引发StopIteration . 当调用send()来启动生成器时,必须使用None作为参数调用它,因为没有可以接收值的yield表达式

但我觉得我错过了一些重要的东西 . 这是我的3 send 调用的示例,包括初始化生成器的初始值为 None 的值:

def multiplier():
    while True:
        m = yield               # Line #3
        print('m = ' + str(m))  # Line #4
        yield str(m * 2)        # Line #5
        yield str(m * 3)        # Line #6

#------------------------

it = multiplier()

print('it.send(None): ')
print(str(it.send(None)))
print('--------------')

print('it.send(10): ')
print(it.send(10))
print('--------------')

print('it.send(100): ')
print(it.send(100))
print('--------------')

这是一个输出:

it.send(None): 
None
--------------
it.send(10): 
m = 10
20
--------------
it.send(100): 
30
--------------

Questions:

  • 当我在第5行使用 it.send(10) 时会发生什么 . 如果我们遵循定义,则恢复生成器执行 . Generator接受 10 作为输入值,并在当前 yield expression 中使用它 . 在我的例子中是 yield str(m * 2) ,但是 m 如何设置为 10 . 什么时候发生的 . 那是因为 myield 之间在#3行中的引用?

  • 在第6行 it.send(10) 中发生了什么,为什么输出仍然是 30 ?这是否意味着我之前的问题中的参考只能使用一次?

Note: 如果我稍微更改我的示例并在第5行和第6行之间添加一行 m = yield 然后在 print(it.send(10)) 之后使用 next(it) - 在这种情况下输出开始有意义:20和300

1 回答

  • 2

    您的生成器函数有三个 yield 表达式,但是're throwing away the value from two of them (lines 5 and 6). If you did something with the values there, you' d看到函数中使用的 100 . 如果您继续运行示例,则第五次调用 send 将导致生成器将 m 更新为新值 .

    让我们在您的示例中查看执行 send 调用的代码,并查看生成器同时执行的操作:

    it = multiplier()
    

    此时,生成器对象已创建并保存到 it . 生成器代码还没有开始运行,它是_2388701的代码 .

    print(str(it.send(None)))
    

    这开始运行生成器函数的代码 . 发送的值必须是 None 或者'll get an error. The function never sees that value. It'更常见的是使用 next 来启动发生器,因为 next(it) 相当于 it.send(None) .

    生成器函数一直运行到第3行,第一个 yield 出现在第3行 . 由于您没有产生任何特定值,因此 send 的返回值为 None (将被打印) .

    print(it.send(10))
    

    这个值被发送到生成器并成为第3行 yield 表达式的值 . 所以 10 被存储为 m ,代码在第4行打印出来 . 生成器函数继续运行到第5行,它到达下一行 yield 表达 . 由于它正在产生 str(m * 2) ,调用代码得到 "20" 并打印出来 .

    print(it.send(100))
    

    100值作为第23行 yield 的值发送到生成器中 . 该值被忽略,因为您没有将 yield 用作表达式而是用作语句 . 就像将 100 单独放在一条线上一样,这是完全合法的,但可能不是很有用 . 代码继续到第5行,它产生 str(m * 3)"30" ,它由调用代码打印 .

    这就是你的驱动代码停止的地方,但是生成器仍处于活动状态,你可以向它发送更多的值(并获得更多的值) . 你对发生器的下一个值也会被忽略,就像 100 那样,但是当生成器中的 while 循环返回到顶部并且达到第3行 yield 时,之后的值将以新的 m 值结束 .

    我怀疑你在这段代码中对 send 的一些困惑与你使用 yield 作为表达式和语句的事实有关 . 可能你不关心发送到发生器的所有值,或者你不关心它们中的任何一个 . 如果要同时生成多个值(如 n*2n*3 ),则可以生成元组而不是单个项 .

    这是您的代码的修改版本,我认为您可能更容易玩和理解:

    def multiplier():
        print("top of generator")
        m = yield # nothing to yield the first time, just a value we get
        print("before loop, m =", m)
        while True:
            print("top of loop, m =", m)
            m = yield m * 2, m * 3         # we always care about the value we're sent
            print("bottom of loop, m =", m)
    
    print("calling generator")
    it = multiplier()
    
    print("calling next")
    next(it)   # this is equivalent to it.send(None)
    
    print("sending 10")
    print(it.send(10))
    
    print("sending 20")
    print(it.send(20))
    
    print("sending 100")
    print(it.send(100))
    

相关问题