我正在玩生成器和生成器表达式,我不完全确定我理解它们是如何工作的(some reference material):
>>> a = (x for x in range(10))
>>> next(a)
0
>>> next(a)
1
>>> a.send(-1)
2
>>> next(a)
3
所以看起来generator.send
被忽略了。这是有意义的(我猜)因为没有明确的yield
表达式来捕获发送的信息...
然而,
>>> a = ((yield x) for x in range(10))
>>> next(a)
0
>>> print next(a)
None
>>> print next(a)
1
>>> print next(a)
None
>>> a.send(-1) #this send is ignored, Why? ... there's a yield to catch it...
2
>>> print next(a)
None
>>> print next(a)
3
>>> a.send(-1) #this send isn't ignored
-1
我明白这是相当远的,我(目前)不能想到这个用例(所以不要问;)
我主要是探索试图弄清楚这些不同的生成器方法是如何工作的(以及生成器表达式如何工作)。为什么我的第二个例子在产生合理值和None
之间交替?此外,任何人都可以解释为什么我的一个generator.send
被忽略而另一个不被忽略?
答案 0 :(得分:3)
这里的混淆是生成器表达式正在隐藏yield
。这是函数形式:
def foo():
for x in range(10):
yield (yield x)
执行.send()
时,会执行内部yield x
会产生x
。然后表达式求值为.send
的值,下一个收益率得出该值。这里有更清晰的形式:
def foo():
for x in range(10):
sent_value = (yield x)
yield sent_value
因此输出非常可预测:
>>> a = foo()
#start it off
>>> a.next()
0
#execution has now paused at "sent_value = ?"
#now we fill in the "?". whatever we send here will be immediately yielded.
>>> a.send("yieldnow")
'yieldnow'
#execution is now paused at the 'yield sent_value' expression
#as this is not assigned to anything, whatever is sent now will be lost
>>> a.send("this is lost")
1
#now we're back where we were at the 'yieldnow' point of the code
>>> a.send("yieldnow")
'yieldnow'
#etc, the loop continues
>>> a.send("this is lost")
2
>>> a.send("yieldnow")
'yieldnow'
>>> a.send("this is lost")
3
>>> a.send("yieldnow")
'yieldnow'
编辑:示例用法。到目前为止,我见过的最酷的是扭曲的inlineCallbacks
功能。 See here有一篇解释它的文章。它的结尾是它允许你产生在线程中运行的函数,一旦函数完成,twisted就会将函数的结果发送回代码。因此,您可以以非常线性和直观的方式编写严重依赖于线程的代码,而不必在整个地方编写大量的小函数。
有关.send
使用潜在用例的理由的更多信息,请参阅PEP 342(我提供的扭曲示例是异步I / O提供此更改的示例)。
答案 1 :(得分:3)
您有点困惑,因为您实际上是从两个来源生成的:生成器表达式(... for x in range(10))
是一个生成器,但您使用yield
创建另一个源。您可以看到,如果list(a)
[0, None, 1, None, 2, None, 3, None, 4, None, 5, None, 6, None, 7, None, 8, None, 9, None]
,您将获得>>> def gen():
... for x in range(10):
... yield (yield x)
。
您的代码与此相同:
{{1}}
只有内部产量(“产量x”)在发电机中“使用” - 它用作外部产量的值。因此,这个生成器在产生范围的值之间来回迭代,并产生“发送”到那些产量的任何东西。如果你向内部收益发送一些东西,你就会收回它,但是如果你碰巧发送一个偶数迭代,那么发送将被发送到外部收益并被忽略。
答案 2 :(得分:2)
此生成器转换为:
for i in xrange(10):
x = (yield i)
yield x
第二次调用send()/ next()的结果将被忽略,因为你对一个yield的结果什么都不做。
答案 3 :(得分:0)
您编写的生成器等同于更详细:
def testing():
for x in range(10):
x = (yield x)
yield x
正如您在此处所看到的,生成器表达式中隐含的第二个yield
不会保存您传递的值,因此,根据生成器执行被阻止的位置send
可能或者可能不起作用。
答案 4 :(得分:-1)
确实 - send
方法适用于生成器对象,该生成器对象是您明确编写的协同例程的结果。在生成器表达式中很难得到一些意义 - 尽管它有效。
- 编辑 - 我以前写过这篇文章,但它是不完整的,因为生成器表达式中的产量可以跨实现预测 - 尽管在任何PEP中都没有提到。
生成器表达式并不意味着拥有
yield
关键字 - 我是 在这种情况下甚至没有定义行为。我们可以想一个 很少,并得到你的表达发生的事情,以满足 那些“无”来自哪里。但是,假设作为一方 如何在Python中实现产量的效果(可能是 甚至是依赖于实现的),而不是应该如此。
生成器表达式的正确形式,简化为:
(<expr> for <variable> in <sequence> [if <expr>])
因此,对<expr>
中的每个值评估<sequence:
- 不仅yield
不合适,因为您不应该使用它。
yield
和send
方法都应该用于完整的协同例程,例如:
def doubler():
value = 0
while value < 100:
value = 2 * (yield value)
您可以像以下一样使用它:
>>> a = doubler()
>>> # Next have to be called once, so the code will run up to the first "yield"
...
>>> a.next()
0
>>> a.send(10)
20
>>> a.send(20)
40
>>> a.send(23)
46
>>> a.send(51)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>