从 python 2.5 开始,可以将
send()
、throw()
、close()
转换为生成器。在定义的生成器内部,可以通过执行以下操作来“捕获”发送的数据:
def gen():
while True:
x = (yield)
if x == 3:
print('received 3!!')
break
else:
yield x
我想要玩的是做类似的事情:
def gen2():
while True:
yield (yield)
注意到它是一个合法的生成器,可以做一些事情.. 我想弄清楚的第一件事是:
这样写有什么好的用法吗?
当做类似的事情时:
g = gen2()
next(g)
g.send(10) # output: 10
g.send(2) # output: nothing
g.send(3) # output: 3
g.send(44) # output: nothing
为什么每一秒“发送”没有任何作用?
yield (yield)
首先从内部None
产生yield
。然后它从 send
或 next
接收值。内部 yield
评估接收到的值,外部 yield
立即生成该值。
每个
yield
从概念上讲都有两部分:
send
或 next
的调用者。send
或 next
调用接收值。类似地,每个
send
或 next
在概念上都有两部分:
yield
表达式。 (对于 None
,该值为 next
。)yield
表达式接收值。系统中最令人困惑的部分可能是这些部分是交错的。
yield
的两个部分对应于 send
或 next
的两个不同调用,send
或 next
的两个部分对应于两个不同的 yield
。
如果我们看一个简单的例子:
def gen():
print('Not ran at first')
yield (yield)
g = gen() # Step 1
print(next(g)) # Step 2
print(g.send(1)) # Step 3
g.send(2) # Step 4
事情是这样的:
Inside the generator Outside the generator
步骤1
g calls gen()
g returns a generator object
without executing the print
just yet statement.
>>> g
<generator object gen at 0x7efe286d54f8>
第2步
next(g) sends None to g
g receives None, ignores it
(since it is paused at the start
of the function)
g prints ('not ran at first')
g executes the "transmit" phase
of the inner yield, transmitting
None
next(g) receives None
第3步
g.send(1) sends 1 to g
g executes the "receive" phase
of the inner yield, receiving 1
g executes the "transmit" phase
of the outer yield, transmitting 1
g.send(1) receives 1 from g
第4步
g.send(2) sends 2 to g
g executes the "receive" phase
of the outer yield, receiving 2
g reaches the end of gen and raises
a StopIteration
g.send(2) raises the StopIteration
from g
yield
是一个表达式。 表达式的值是使用 .send
发送的任何内容的值,如果没有发送任何内容,则为 None (包括如果使用 next
代替 .send
)。 .send
是一个方法调用,因此当然也会返回一个值,该值是生成器生成的值。 换句话说,每次您 .send
时,都会产生一个值(可能是 None),而每次您 yield
时,都会发送一个值(可能是 None)。
这是一个简单的例子:
def gen():
sent1 = yield 1
print(sent1, "was sent")
sent2 = yield 2
print(sent2, "was sent")
print("Reached end of generator")
g = gen()
print(next(g), "was yielded")
print(g.send("A"), "was yielded")
print(g.send("B"), "was yielded")
next(g)
# output
1 was yielded
A was sent
2 was yielded
B was sent
Reached end of generator
# StopIteration is raised here
在您的示例中,第一个
next
产生 None,因为第一个 yield
是 yield (yield)
中的内部产量(即括号中的那个)。 第一个 send
传递 10 作为此 yield
的值。 您send
的每个后续值都会成为其中一个收益率的值。 您的某些 send
调用不产生输出的原因是内部收益未指定任何值,因此它产生 None
。 如上所述,当你调用 send
时,会产生一个值;在您的情况下,内部产量的该值为 None ,因此交互式提示中不会显示任何输出。 另一方面,外部收益率确实指定了一个值,即内部收益率的结果。 因此,当您将一个值放入内部 send
时,它将在下一次迭代时由外部 yield
产生。 (我假设您指的是交互式提示符处的输出;如果您将代码作为脚本运行,则根本不会有输出,因为您永远不会 yield
任何内容或以其他方式产生显式输出。)这是另一个可能具有启发性的例子:
print
请注意,每次发送一个值时,它都会立即返回。 这是因为每个
def gen():
yield (yield (yield (yield "WHOA")))
>>> g = gen()
>>> next(g)
'WHOA'
>>> g.send(1)
1
>>> g.send(2)
2
>>> g.send(3)
3
>>> g.send(4)
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
g.send(4)
StopIteration
都会产生更深层嵌套的
yield
的值。 每个 yield
“成为”发送的值,并立即由链中的下一个 yield
产生。 这将持续下去,直到所有收益都耗尽并引发 StopIteration。之前已经问过类似的问题。 我的印象是,由于人们期望
yield
“只发送”一个值,因此往往会出现混乱。 但事实并非如此。 使用
send
推进生成器并产生下一个结果,就像使用 send
一样。 您可以将 next
视为等同于 next(gen)
。很困惑”!反对KISS!)