【问题标题】:Python generator send: don't yield a new value after a sendPython生成器发送:发送后不产生新值
【发布时间】:2014-03-28 13:03:33
【问题描述】:

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

我有一个像这样的生成器,它充当 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。

我知道我可以用一种丑陋的方式解决这个问题,只在收到发送后产生一个垃圾值,但我试图让我的代码保持优雅,而事实恰恰相反。有没有办法告诉生成器我还不需要新值?

谢谢。

【问题讨论】:

  • 我不确定问题出在哪里。从服务器获取数据与向其发送数据不同。你真的需要两个协程吗?
  • 不幸的是,是的,当我的协程是这种半迭代器半协程的“复合”风格时,我经常在我的协程中放入裸露的 yield 语句。我在将此类消费者重写为明确的send all 方面取得了一些成功,即使我大部分时间都在发送None。不确定这是否对您有帮助,但这是一个想法。
  • @IfLoop 但是与服务器通信与与服务器通信是一回事。通信的方向已经清楚地表达为数据进出协程的方向。为什么要拆分?
  • 因为 python 中的生成器没有不同的 send() 和 next(),所以 send() 只是带​​有参数的 next()。

标签: python generator yield coroutine yield-keyword


【解决方案1】:

看起来问题出在您每次迭代调用生成器两次,一次使用 .send(None)(在 for 循环中),一次使用 .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 好多少。我希望有一个替代方法调用或库可以处理这个问题。
【解决方案2】:

我也刚遇到这个问题,也没有找到比 dummy 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-30
    • 1970-01-01
    • 2012-09-20
    • 2012-11-22
    • 2016-09-26
    • 1970-01-01
    相关资源
    最近更新 更多