我理解让步。但是生成器的send函数是做什么的呢?文件说:
generator.send(值)
恢复执行并向生成器函数“发送”一个值。value参数变成当前yield表达式的结果。send()方法返回生成器产生的下一个值,如果生成器退出而没有产生另一个值,则引发StopIteration。
这是什么意思?我以为value是生成器函数的输入?短语“send()方法返回生成器产生的下一个值”似乎也是yield的确切目的,它也返回生成器产生的下一个值。
有没有一个生成器利用send完成了yield不能完成的事情的例子?
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(),则结果将是传递给该方法的值。
这些也让我很困惑。下面是我做的一个例子,当我试图建立一个以交替顺序(屈服,接受,屈服,接受)产生和接受信号的生成器时……
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!"
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
这将允许您迭代一个列表,但也动态地发送它的新目标值。
这可能会对某些人有所帮助。这是一个不受发送函数影响的生成器。它在实例化时接受number参数,不受send的影响:
>>> 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来执行相同类型的函数,所以在每次迭代中你都可以改变number的值:
def double_number(number):
while True:
number *= 2
number = yield number
下面是它的样子,你可以看到为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
要获得更多帮助,请查看这个很棒的教程。
它被用来将值发送到一个刚刚产生的生成器中。下面是一个人为的(无用的)解释性例子:
>>> def double_inputs():
... while True:
... x = yield
... yield x * 2
...
>>> gen = double_inputs()
>>> next(gen) # run up to the first yield
>>> gen.send(10) # goes into 'x' variable
20
>>> next(gen) # run up to the next yield
>>> gen.send(6) # goes into 'x' again
12
>>> next(gen) # run up to the next yield
>>> gen.send(94.3) # goes into 'x' again
188.5999999999999
你不能只靠产量来做到这一点。
至于为什么它很有用,我见过的最好的用例之一是Twisted的@defer.inlineCallbacks。本质上,它允许你写出这样的函数:
@defer.inlineCallbacks
def doStuff():
result = yield takesTwoSeconds()
nextResult = yield takesTenSeconds(result * 10)
defer.returnValue(nextResult / 10)
What happens is that takesTwoSeconds() returns a Deferred, which is a value promising a value will be computed later. Twisted can run the computation in another thread. When the computation is done, it passes it into the deferred, and the value then gets sent back to the doStuff() function. Thus the doStuff() can end up looking more or less like a normal procedural function, except it can be doing all sorts of computations & callbacks etc. The alternative before this functionality would be to do something like:
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
它更加复杂和笨拙。