python - 将值发送到 Python 协程而不处理 StopIteration

标签 python generator coroutine

给定一个 Python 协程:

def coroutine():
     score = 0
     for _ in range(3):
          score = yield score + 1

我想像这样在一个简单的循环中使用它:

cs = coroutine()
for c in cs:
     print(c)
     cs.send(c + 1)

...我希望打印

1
3
5

但实际上,我在 yield score + 1 这行遇到异常:

 TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

如果我手动调用 next 就可以让它工作:

c = next(cs)
while True:
    print(c)
    try:
        c = cs.send(c + 1)
    except StopIteration:
        break

但我不喜欢我需要使用 try/except,因为生成器通常非常优雅。

那么,有没有办法在不显式处理 StopIteration 的情况下使用这样的有限协程?我很乐意更改生成器和迭代它的方式。

第二次尝试

Martijn 指出 for 循环和我对 send 的调用都会推进迭代器。很公平。那么,为什么我不能在协程循环中使用两个 yield 语句来解决这个问题?

def coroutine():
    score = 0
    for _ in range(3):
        yield score
        score = yield score + 1

cs = coroutine()
for c in cs:
    print(c)
    cs.send(c + 1)

如果我尝试这样做,我会得到同样的错误,但是在 send 行。

0
None
Traceback (most recent call last):
  File "../coroutine_test.py", line 10, in <module>
    cs.send(c + 1)
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

最佳答案

我会尝试你的第二次尝试。首先,让coroutine定义为:

def coroutine():
    score = 0
    for _ in range(3):
        yield
        score = yield score + 1

此函数将输出您在原始问题中的 1, 3, 5

现在,让我们将 for 循环转换为 while 循环。

# for loop
for c in cs:
    print(c)
    cs.send(c + 1)

# while loop
while True:
    try:
        c = cs.send(None)
        print(c)
        cs.send(c + 1)
    except StopIteration:
        break

现在,如果我们在 next(cs) 之前使用以下代码,我们就可以使这个 while 循环工作。总计:

cs = coroutine()
next(cs)
while True:
    try:
        c = cs.send(None)
        print(c)
        cs.send(c + 1)
    except StopIteration:
        break
# Output: 1, 3, 5

当我们尝试将其转换回 for 循环时,我们有相对简单的代码:

cs = coroutine()
next(cs)
for c in cs:
    print(c)
    cs.send(c + 1)

这会根据需要输出 1, 3, 5。问题是在 for 循环的最后一次迭代中,cs 已经用完了,但是再次调用了 send。那么,我们如何从生成器中获得另一个 yield 呢?让我们在最后加一个...

def coroutine():
    score = 0
    for _ in range(3):
        yield
        score = yield score + 1
    yield

cs = coroutine()
next(cs)
for c in cs:
    print(c)
    cs.send(c + 1)
# Output: 1, 3, 5

最后一个示例按预期迭代,没有 StopIteration 异常。

现在,如果我们退后一步,这一切都可以更好地写成:

def coroutine():
    score = 0
    for _ in range(3):
        score = yield score + 1
        yield # the only difference from your first attempt

cs = coroutine()
for c in cs:
    print(c)
    cs.send(c + 1)
# Output: 1, 3, 5

注意 yield 是如何移动的,next(cs) 是如何被删除的。

关于python - 将值发送到 Python 协程而不处理 StopIteration,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35469386/

相关文章:

python - 如何在 Windows 7 的 virtualenv 中安装 web2py?

python - 如何将列表值与字典键进行比较并使用 python 创建一个新字典

python - dict_keys 是生成器函数吗?

python - Gevent可以和CPython中的真实线程结合使用吗?

python - 为什么我们需要 python 中的协程?

go - 如何决定并发操作的数量?

python - 我如何知道 pywikibot 的特定索引模板参数为空?

python - KeyboardInterrupt - 复制数据是否安全

javascript - 使用子进程在 Node.js 中进行复杂的数据操作

python - 嵌套生成器和 yield 来自?