Python生成器send:在发送后不要yield一个新值

10

这是一个有点奇怪的问题,所以我来解释一下:

我有一个像这样的生成器,它作为IRC服务器的前端:

def irc_iter(): # not the real code, simplified
    msgs = get_msgs()
    for msg in msgs:
        if is_ping(msg):
            pong()
        else:
            to_send = yield msg
            for s in to_send:
                send(s)

理论上,这应该让我做一些很酷的事情,比如:

server = connect()
for line in server:
       if should_respond(line):
           server.send('WOW SUCH MESSAGE')

然而,这里有一个问题:generator.send 也会产出下一个值。这意味着server.send也会把下一个消息传递给我...而我更希望像处理其他所有被产出的line消息那样来处理它。

我知道我可以用一种丑陋的方式解决这个问题,即在接收到send后仅产生一个垃圾值,但我试图保持我的代码简洁优雅,而这恰好相反。有没有一种方法可以告诉生成器我暂时不想要新值?

谢谢。


我不确定我看到问题所在。从服务器获取数据的思路和向服务器发送数据的思路并不相同。你真的需要两个协程吗? - SingleNegationElimination
1
不幸的是,是的,当这些半迭代器、半协程的“组合”风格时,我经常会在我的协程中放置裸的yield语句。我已经成功地将这样的消费者重写为明确地send所有时间,即使大多数时间我发送None。不确定这是否对你有帮助,但这是一个想法。 - roippi
@IfLoop 但是与服务器通信就是与服务器通信。通信的方向已经通过协程中数据的方向清晰地表达出来了。为什么要将其拆分? - vgel
因为Python中的生成器没有明确的send()next(),所以send()只是带有参数的next() - SingleNegationElimination
2个回答

3

我最近也遇到了这个问题,但并没有找到比虚拟的yield更好的解决办法...

所以在你的生成器代码中:

        # Input
        input_value = yield
        if input_value is None:
            # Handle situation if someone used your generator in a wrong way
            sys.exit(-1)
        yield "DUMMY YIELD JUST STOP HERE!!!"

在客户端代码中:

while True:
    values = next(my_generator)
    ...
    if values == 'INPUT_NEEDED':
        # At this point you realize it's input time
        next(my_generator) # Rewind to the input point
        my_generator.send(12345) # Returns "DUMMY YIELD JUST STOP HERE!!!"
        continue
    # Continue the main loop

1
看起来问题出在你每次迭代时两次调用生成器,一次是在for循环中使用.send(None),另一次是使用.send(response)。
如果for循环能够迭代.send()而不是.next(),那么这个问题就可以简单地解决,但我不熟悉任何让它工作的方法(PEP342中的可选扩展continue语句?),除非将其包装在另一个生成器中(可能使用队列将值推入到.next()调用中的.send())。最简单的解决方案可能是:
server = connect()
response = None 
try:
    while True:
        line = server.send(response)
        response = None
        if should_respond(line):
            response ='WOW SUCH MESSAGE'
except StopIteration:
    pass

那个解决方案可行,但并没有比使用虚拟的 yield 更好。我希望有一个替代方法或库可以处理这个问题。 - vgel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接