python生成器“发送”函数的目的是什么?

有人可以给我一个为什么与Python生成器函数关联的“发送”函数存在的例子吗? 我完全理解yield函数。 但是,发送function令我感到困惑。 关于这个方法的文档是错综复杂的:

generator.send(value) 

恢复执行并将“值”发送到生成器函数中。 价值论证成为当前收益expression式的结果。 send()方法返回发生器产生的下一个值,或者如果发生器退出而不产生另一个值则产生StopIteration。

那是什么意思? 我认为价值是function的input? 短语“send()方法返回生成器生成的下一个值”似乎也是yield函数的确切目的; 产量返回由发生器产生的下一个值…

有人能给我一个发电机利用发送的例子,实现一些收益不能?

它用于将值发送到刚生成的生成器。 这是一个人造的(无用的)解释性例子:

 >>> def double_inputs(): ... while True: ... x = yield ... yield x * 2 ... >>> gen = double_inputs() >>> gen.next() #run up to the first yield >>> gen.send(10) #goes into 'x' variable 20 >>> gen.next() #run up to the next yield >>> gen.send(6) #goes into 'x' again 12 >>> gen.next() #run up to the next yield >>> gen.send(94.3) #goes into 'x' again 188.5999999999999 

你不能这样做,只有yield

至于为什么它是有用的,我见过的最好用例之一是Twisted's @defer.inlineCallbacks 。 从本质上讲,它可以让你写一个这样的function:

 @defer.inlineCallbacks def doStuff(): result = yield takesTwoSeconds() nextResult = yield takesTenSeconds(result * 10) defer.returnValue(nextResult / 10) 

会发生什么是takesTwoSeconds()返回一个Deferred ,这是一个值,承诺一个值将在稍后计算。 Twisted可以在另一个线程中运行计算。 计算完成后,将其传递给延迟,然后将值发送回doStuff()函数。 因此, doStuff()最终可能看起来像一个正常的过程函数,除了它可以做各种计算和callback等。在这个function之前的替代方法是做类似的事情:

 def doStuff(): returnDeferred = defer.Deferred() def gotNextResult(nextResult): returnDeferred.callback(nextResult / 10) def gotResult(result): takesTenSeconds(result * 10).addCallback(gotNextResult) takesTwoSeconds().addCallback(gotResult) return returnDeferred 

这是更复杂和笨拙。

这个函数是编写协程

 def coroutine(): for i in range(1, 10): print("From generator {}".format((yield i))) c = coroutine() c.send(None) try: while True: print("From user {}".format(c.send(1))) except StopIteration: pass 

版画

 From generator 1 From user 2 From generator 1 From user 3 From generator 1 From user 4 ... 

看看控制是如何来回传递的? 那些是协程。 他们可以用于各种酷的东西,如asynchronousIO和类似的东西。

想想这样,用发电机,不发送,这是一条单行道

 ========== yield ======== Generator | ------------> | User | ========== ======== 

但是发送,它变成了一条双向的街道

 ========== yield ======== Generator | ------------> | User | ========== <------------ ======== send 

这为用户定制发电机行为以及发电机响应用户打开了大门。

send方法执行协程 。

如果你还没有遇到过协程,那么他们很难绕开你的脑袋,因为它们改变了程序的stream程。 你可以阅读一个很好的教程了解更多细节。

这可能有助于某人。 这是一个不受发送function影响的发生器。 它在实例化中取得数字参数,不受发送的影响:

 >>> def double_number(number): ... while True: ... number *=2 ... yield number ... >>> c = double_number(4) >>> c.send(None) 8 >>> c.next() 16 >>> c.next() 32 >>> c.send(8) 64 >>> c.send(8) 128 >>> c.send(8) 256 

现在,您将如何使用send来完成同一types的function,因此在每次迭代中,您都可以更改number的值:

 def double_number(number): while True: number *= 2 number = yield number 

这是看起来像,你可以看到发送一个新的价值数字改变的结果:

 >>> def double_number(number): ... while True: ... number *= 2 ... number = yield number ... >>> c = double_number(4) >>> >>> c.send(None) 8 >>> c.send(5) #10 10 >>> c.send(1500) #3000 3000 >>> c.send(3) #6 6 

你也可以把它放在for循环中,如下所示:

 for x in range(10): n = c.send(n) print n 

如需更多帮助,请查看这个伟大的教程 。

一些用例使用generator和send()

带有send()生成器允许:

  • 记住执行的内部状态
    • 我们在做什么?
    • 我们的数据目前状况如何?
  • 返回值序列
  • 接收input序列

这里有一些用例:

看着试图遵循食谱

让我们有一个配方,它需要按照某种顺序预定义一组input。

我们可能:

  • 从配方创build一个watched_attempt实例
  • 让它得到一些input
  • 每个input都会返回有关当前在jar中的信息
  • 与每个input检查,input是预期的(如果不是,则会失败)

     def recipe(): pot = [] action = yield pot assert action == ("add", "water") pot.append(action[1]) action = yield pot assert action == ("add", "salt") pot.append(action[1]) action = yield pot assert action == ("boil", "water") action = yield pot assert action == ("add", "pasta") pot.append(action[1]) action = yield pot assert action == ("decant", "water") pot.remove("water") action = yield pot assert action == ("serve") pot = [] yield pot 

要使用它,首先创buildwatched_attempt实例:

 >>> watched_attempt = recipe() >>> watched_attempt.next() [] 

.next()的调用是开始执行生成器所必需的。

返回值显示,我们的锅目前是空的。

现在按照配方预期做几个动作:

 >>> watched_attempt.send(("add", "water")) ['water'] >>> watched_attempt.send(("add", "salt")) ['water', 'salt'] >>> watched_attempt.send(("boil", "water")) ['water', 'salt'] >>> watched_attempt.send(("add", "pasta")) ['water', 'salt', 'pasta'] >>> watched_attempt.send(("decant", "water")) ['salt', 'pasta'] >>> watched_attempt.send(("serve")) [] 

正如我们所看到的,这个锅最终是空的。

如果一个人不遵循这个秘诀,它就会失败(所看到的尝试做什么的结果会是什么样的 – 只是学习我们在给出指示时没有给予足够的重视。

 >>> watched_attempt = running.recipe() >>> watched_attempt.next() [] >>> watched_attempt.send(("add", "water")) ['water'] >>> watched_attempt.send(("add", "pasta")) --------------------------------------------------------------------------- AssertionError Traceback (most recent call last) <ipython-input-21-facdf014fe8e> in <module>() ----> 1 watched_attempt.send(("add", "pasta")) /home/javl/sandbox/stack/send/running.py in recipe() 29 30 action = yield pot ---> 31 assert action == ("add", "salt") 32 pot.append(action[1]) 33 AssertionError: 

请注意,

  • 有预期步骤的线性序列
  • 步骤可能会有所不同(有些正在删除,有些正在添加到锅中)
  • 我们设法通过一个函数/生成器来完成所有这些工作 – 不需要使用复杂的类或类似的结构。

运行总计

我们可以使用生成器来跟踪发送给它的值的总数。

任何时候,我们添加一个数字,input和总数的计数返回(有效的前一刻input发送到它)。

 from collections import namedtuple RunningTotal = namedtuple("RunningTotal", ["n", "total"]) def runningtotals(n=0, total=0): while True: delta = yield RunningTotal(n, total) if delta: n += 1 total += delta if __name__ == "__main__": nums = [9, 8, None, 3, 4, 2, 1] bookeeper = runningtotals() print bookeeper.next() for num in nums: print num, bookeeper.send(num) 

输出将如下所示:

 RunningTotal(n=0, total=0) 9 RunningTotal(n=1, total=9) 8 RunningTotal(n=2, total=17) None RunningTotal(n=2, total=17) 3 RunningTotal(n=3, total=20) 4 RunningTotal(n=4, total=24) 2 RunningTotal(n=5, total=26) 1 RunningTotal(n=6, total=27) 

这些也让我困惑。 下面是一个例子,我试图设置一个发生器产生和接受交替的顺序(yield,accept,yield,accept)的信号。

 def echo_sound(): thing_to_say = '<Sound of wind on cliffs>' while True: thing_to_say = (yield thing_to_say) thing_to_say = '...'.join([thing_to_say]+[thing_to_say[-6:]]*2) yield None # This is the return value of send. gen = echo_sound() print 'You are lost in the wilderness, calling for help.' print '------' in_message = gen.next() print 'You hear: "{}"'.format(in_message) out_message = 'Hello!' print 'You yell "{}"'.format(out_message) gen.send(out_message) print '------' in_message = gen.next() print 'You hear: "{}"'.format(in_message) out_message = 'Is anybody out there?' print 'You yell "{}"'.format(out_message) gen.send(out_message) print '------' in_message = gen.next() print 'You hear: "{}"'.format(in_message) out_message = 'Help!' print 'You yell "{}"'.format(out_message) gen.send(out_message) 

输出是:

 You are lost in the wilderness, calling for help. ------ You hear: "<Sound of wind on cliffs>" You yell "Hello!" ------ You hear: "Hello!...Hello!...Hello!" You yell "Is anybody out there?" ------ You hear: "Is anybody out there?...there?...there?" You yell "Help!"