【发布时间】:2023-03-26 22:57:01
【问题描述】:
更新:我已经启动了一个 thread on python-ideas 来为此目的提出额外的语法或 stdlib 函数(即指定 yield from 发送的第一个值)。到目前为止 0 回复... :/
如何截取子生成器的第一个产生的值,但使用 yield from 将剩余的迭代委托给后者?
例如,假设我们有一个任意的双向生成器subgen,并且我们想将它包装在另一个生成器gen 中。 gen 的目的是拦截subgen 的第一个产生的值,并将其余的生成——包括发送的值、抛出的异常、.close() 等——委托给子-发电机。
首先想到的可能是:
def gen():
g = subgen()
first = next(g)
# do something with first...
yield "intercepted"
# delegate the rest
yield from g
但是这是错误的,因为当调用者.sends 得到第一个值后返回给生成器时,它最终会成为yield "intercepted" 表达式的值,它被忽略,取而代之的是g将接收None 作为第一个.send 值,作为yield from 语义的一部分。
所以我们可能会考虑这样做:
def gen():
g = subgen()
first = next(g)
# do something with first...
received = yield "intercepted"
g.send(received)
# delegate the rest
yield from g
但是我们在这里所做的只是将问题向后移了一步:一旦我们调用g.send(received),生成器就会恢复执行并且不会停止,直到它到达下一个 yield 语句,其值变为.send 调用的返回值。所以我们还必须拦截并重新发送它。然后发送that,再发送that,以此类推……所以这样不行。
基本上,我要求的是yield from,它可以自定义发送到生成器的第一个值是什么:
def gen():
g = subgen()
first = next(g)
# do something with first...
received = yield "intercepted"
# delegate the rest
yield from g start with received # pseudocode; not valid Python
...但不必自己重新实现yield from 的所有语义。也就是说,费力且维护性差的解决方案是:
def adaptor(generator, init_send_value=None):
send = init_send_value
try:
while True:
send = yield generator.send(send)
except StopIteration as e:
return e.value
这基本上是对yield from 的错误重新实现(它缺少对throw、close 的处理)。理想情况下,我想要一些更优雅、更少冗余的东西。
【问题讨论】:
-
是
xNone 在你这样做之后:x = yield 42? -
不一定,
x可以是调用者发送的任何内容。使用 Python 3.9 -
你用的是什么 Python?调用者发送的任何东西怎么能是 x 呢?
-
我使用的是 Python 3.9。例如,如果直接使用
subgen:g = subgen(); v = next(g); v = g.send(123)。在上一条语句中,我们向subgen发送了123,所以x是123。然后生成器到达下一个yield语句并产生x + 2,即125;所以v现在是125。请记住,第一个send只是用于初始化生成器(即它的值不会出现在生成器中的任何位置)并且必须始终为.send(None),或等效的next()。 -
见here。 “当调用 send() 来启动生成器时,必须使用 None 作为参数调用它,因为没有 yield 表达式可以接收该值。”但是,在那之后,“value 参数变成了当前 yield 表达式的结果。”
标签: python generator yield-from