我理解让步。但是生成器的send函数是做什么的呢?文件说:

generator.send(值) 恢复执行并向生成器函数“发送”一个值。value参数变成当前yield表达式的结果。send()方法返回生成器产生的下一个值,如果生成器退出而没有产生另一个值,则引发StopIteration。

这是什么意思?我以为value是生成器函数的输入?短语“send()方法返回生成器产生的下一个值”似乎也是yield的确切目的,它也返回生成器产生的下一个值。

有没有一个生成器利用send完成了yield不能完成的事情的例子?


当前回答

这些也让我很困惑。下面是我做的一个例子,当我试图建立一个以交替顺序(屈服,接受,屈服,接受)产生和接受信号的生成器时……

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!"

其他回答

send()方法控制yield表达式左边的值。

为了理解yield的不同之处以及它所代表的值,让我们首先快速刷新python代码被求值的顺序。

第6.15节评估命令

Python从左到右计算表达式。注意,在计算赋值时,右边的值比左边的值先计算。

所以表达式a = b首先求右边的值。

如下所示,a[p('left')] = p('right')首先计算右边的值。

>>> def p(side):
...     print(side)
...     return 0
... 
>>> a[p('left')] = p('right')
right
left
>>> 
>>> 
>>> [p('left'), p('right')]
left
right
[0, 0]

收益率是做什么的?, yield将暂停函数的执行并返回给调用者,并在暂停之前停止的同一位置恢复执行。

死刑到底在哪里暂缓执行?你可能已经猜到了…… 在yield表达式的左右之间暂停执行。因此new_val = yield old_val执行在=号处停止,并且右边的值(挂起之前,也是返回给调用者的值)可能与左边的值(恢复执行后分配的值)不同。

Yield产生两个值,一个在右边,另一个在左边。

如何控制yield表达式左边的值?通过.send()方法。

6.2.9. 产量表达式

恢复后的yield表达式的值取决于恢复执行的方法。如果使用__next__()(通常通过for或next()内置函数),则结果为None。否则,如果使用send(),则结果将是传递给该方法的值。

send方法实现协程。

如果你没有遇到过协程,你很难理解它们,因为它们改变了程序的运行方式。你可以阅读一篇好的教程了解更多细节。

使用generator和send()的一些用例

使用send()的生成器允许:

记住执行的内部状态 我们走到哪一步了 我们数据的当前状态是什么 返回值序列 接收输入序列

下面是一些用例:

观察尝试遵循食谱

让我们有一个配方,它期望以某种顺序预先定义一组输入。

我们可以:

create a watched_attempt instance from the recipe let it get some inputs with each input return information about what is currently in the pot with each input check, that the input is the expected one (and fail if it is not) 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

要使用它,首先创建watched_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:

注意,:

预期的步骤有一个线性序列 步骤可能会有所不同(有些是移除,有些是添加到锅中) 我们通过函数/生成器来实现这一切,不需要使用复杂的类或类似的东西 结构。

运行总数

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

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

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)

itr.send(None)和next(itr)是一样的你所做的是在生成器中给出yield给出的值。

下面的例子清楚地说明了这一点,以及如何更实际地使用它。

def iterator_towards(dest=100):
    value = 0
    while True:
        n = yield value
        if n is not None:
            dest = n
        if dest > value:
            value += 1
        elif dest < value:
            value -= 1
        else:
            return

num = iterator_towards()
for i in num:
    print(i)
    if i == 5:
        num.send(0)

这将打印:

0
1
2
3
4
5
3
2
1
0

i == 5处的代码告诉它发送0。这不是iterator_toward中的None,所以它改变了dest的值,然后我们迭代到0。

但是请注意,在值5之后没有值4。这是因为.send(0)的性质是它被输出值为4,而不输出。

如果我们添加一个continue,我们可以重新得到相同的值。

def iterator_towards(dest=100):
    value = 0
    while True:
        n = yield value
        if n is not None:
            dest = n
            continue
        if dest > value:
            value += 1
        elif dest < value:
            value -= 1
        else:
            return

这将允许您迭代一个列表,但也动态地发送它的新目标值。

这个函数用来写协程

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
...

看到控件是如何来回传递的了吗?这些是协程。它们可以用于各种很酷的事情,如异步IO和类似的。

想象一下,有一个发电机,没有发送器,这是一条单行道

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

但是有了send,它就变成了双向的

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

这为用户打开了一扇门,用户可以动态地定制生成器的行为和生成器对用户的响应。