给定一个 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/