我理解让步。但是生成器的send函数是做什么的呢?文件说:
generator.send(值)
恢复执行并向生成器函数“发送”一个值。value参数变成当前yield表达式的结果。send()方法返回生成器产生的下一个值,如果生成器退出而没有产生另一个值,则引发StopIteration。
这是什么意思?我以为value是生成器函数的输入?短语“send()方法返回生成器产生的下一个值”似乎也是yield的确切目的,它也返回生成器产生的下一个值。
有没有一个生成器利用send完成了yield不能完成的事情的例子?
这个函数用来写协程
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
这为用户打开了一扇门,用户可以动态地定制生成器的行为和生成器对用户的响应。
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 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
它更加复杂和笨拙。
这个函数用来写协程
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
这为用户打开了一扇门,用户可以动态地定制生成器的行为和生成器对用户的响应。
这可能会对某些人有所帮助。这是一个不受发送函数影响的生成器。它在实例化时接受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
要获得更多帮助,请查看这个很棒的教程。