将值发送到压缩生成器

Sending values to zipped generators

考虑以下代码:

def g1():
    while True:
         yield 1

def g2():
    while True:
        yield 1
        yield 2

def g3():
    while True:
        yield 1
        yield 2
        yield 3

def G():
    # Zip all three generators
    genList = [g1(), g2(), g3()]
    Z = zip(*genList)
    yield from Z

# Iterate over the zip of all three generators
for i, g in zip(range(6), G()):
    print(g)

三个生成器 g1、g2、g3 都生成简单数字,外部生成器 G 从这三个生成器的 zip 生成。输出最终看起来像这样:

(1, 1, 1)
(1, 2, 2)
(1, 1, 3)
(1, 2, 1)
(1, 1, 2)
(1, 2, 3)

这正是我所期望的:三个生成器在 "concurrently" 上迭代,也就是说我没有用完第一个就可以进入下一个。

但是,我担心这有点乱。它之所以有效,是因为 python 中的 zip 类型实现了 __next__ 方法,我认为在迭代 G() 生成器时会调用该方法。

但是假设我想将值发送到我的生成器?即

# stop iteration if a negative value is sent
def g1():
    yield
    while True:
         input = yield 1
         if input < 0:
             break
...

# Create the generator and prime it
gen = G()
next(gen)

for i in zip(range(6)):
    g = gen.send(1)
    print(g)

gen.send(-1)

zip 类型没有实现 send 功能,我不太确定它是如何实现的。

应该说,我对生成器、协程,更重要的是 python 中新的 yield from 语法的体验非常少。我实际上担心我试图在没有真正理解它的意图的情况下将这些语言特性硬塞进我的代码中。

这些函数的最终目的将是 return 一个与音频循环对应的字符串容器; g1 生成器每次都会产生相同的循环,g2 生成器会交替出现,等等。这些变化会导致每次迭代时都会 return 编辑不同的容器。

将每个循环视为自己的协程似乎是有意义的,因为它独立于其他循环推进,我想尝试利用一些语言特性,而不是想出一个不太便携的面向对象解决方案。

但是,也许 itertools 中有我可以利用的东西,或者可能是使用上下文管理器的东西。

send 只是从 yield 恢复,与 next 调用完全相同,但传递了一个值。如果您使用 next 而不是 send,这就像发送 None

您可以使用它来停止生成器的执行:

value = yield x
if value:
    return

只要您对其进行迭代(通过 zip、for 循环或 next),它将保持 运行,但当您 send 时停止在任何真值。但是,我想知道你为什么想要。你觉得哪里需要做这样的事情?

另请注意,itertools.cycle 已经完成了您在这里尝试的操作:

import itertools
c = itertools.cycle([1, 2, 3])
next(c), next(c), next(c), next(c)
# 1, 2, 3, 1, ...