【问题标题】:yield-based equivalent to Python3 'yield from' delegation without losing send基于收益的等效于 Python3 'yield from' 委托而不丢失发送
【发布时间】:2018-07-13 00:55:08
【问题描述】:

我不知道如何使用yield(不是yield from)来包装子生成器而不阻止 send() 工作。使用yield from 允许 send() 继续使用子生成器,但它会移交控制权,您无法检查或计算通过的值。

动机:我使用 python3 生成器编写了一些流迭代,允许文件或套接字或任何东西通过一个通用的“接口”一次读取一个字节,以便解析器一个一个地使用字节。

为了帮助解析,我扩展了生成器逻辑,以便允许请求者可以指示流是否应该在产生字节后增加其位置(字节被读取并消耗),或者流是否应该在产生后保持其位置(偷看 - 字节只被读取)。这样可以在将流传递给子解析器之前匹配规则)。

在当前的实现中,以下任何一个都会从生成器中获取一个字节,并增加生成器在流中的位置。

byte = stream.send(True)

byte = next(stream)

...虽然这个特殊调用从生成器中获取一个字节,但不会增加流中的位置。

byte = stream.send(False)

到目前为止一切顺利。我的低内存 JSON 解析器 (https://github.com/ShrimpingIt/medea) 运行良好。 https://github.com/ShrimpingIt/medea/blob/dd0007e657cd487913c72993dcdaf0f60d8ee30e/examples/scripts/twitterValuesNamed.py 的示例能够处理来自文件的缓存推文。

在 HTTPS 的情况下 - 要从套接字获取实时推文,SSL 套接字不会在最后自动关闭。任何解析过程都只是挂起等待更多数据,我想修复它。

出于这个原因,我首先创建 HTTPS 流,然后从流中读取字节,处理 content-length 标头并跳到 HTTP 标头末尾的“\r\n\r\n”,然后再处理在正确的位置流到解析器。至此,我知道流在停止之前应该提供多少内容字节。

不幸的是,我遇到了语法、表达能力或理解问题。

直接移交给流很容易,并且保留了发送功能(允许偷看字节)...

def delegatingStream():
    yield from rawStream

但是,我不知道如何创建一个迭代器,它智能地使用 contentLength 值在 contentLength 之后终止而不中断 send()。

我需要使用yield 才能干预迭代器逻辑,但似乎只有yield from 允许send() 被委派。我什至无法将 send 和 yield 放在一起来复制 delegatingStream() 的行为。例如,这没有相同的效果。

def relayingStream():
    while True:
        yield rawStream.send((yield))

避免yield from的原因是我最终需要有一个这样的实现,(这也不会忠实地转发send())......

def terminatingStream():
    contentPos = 0
    while contentPos < contentLength:
        increment = (yield)
        yield rawStream.send(increment)
        if increment is not False:
            contentPos += 1
    rawStream.throw(StopIteration)

知道如何在不使用yield from 的情况下正确转发来自send() 的值吗?

【问题讨论】:

    标签: python generator send yield delegation


    【解决方案1】:

    您可以咨询PEP 380 -- Syntax for Delegating to a Subgenerator,在Formal Semantics section 中查看yield from Python 等效项。

    RESULT = yield from EXPR 本质上等同于:

    _i = iter(EXPR)
    try:
        _y = next(_i)
    except StopIteration as _e:
        _r = _e.value
    else:
        while 1:
            try:
                _s = yield _y
            except GeneratorExit as _e:
                try:
                    _m = _i.close
                except AttributeError:
                    pass
                else:
                    _m()
                raise _e
            except BaseException as _e:
                _x = sys.exc_info()
                try:
                    _m = _i.throw
                except AttributeError:
                    raise _e
                else:
                    try:
                        _y = _m(*_x)
                    except StopIteration as _e:
                        _r = _e.value
                        break
            else:
                try:
                    if _s is None:
                        _y = next(_i)
                    else:
                        _y = _i.send(_s)
                except StopIteration as _e:
                    _r = _e.value
                    break
    RESULT = _r
    

    您可以完全实现来替换yield from。您可能会放弃 RESULT 处理,因为您不期望任何处理。接下来是generator.close()generator.throw()的细心处理;如果您假设这些存在,那么您可以进一步简化,并使用一些更易读的名称:

    it = iter(EXPR)
    try:
        value = next(it)
    except StopIteration:
        pass
    else:
        while True:
            try:
                sent = yield value
            except GeneratorExit:
                it.close()
                raise
            except BaseException:
                try:
                    value = it.throw(*sys.exc_info())
                except StopIteration:
                    break
            else:
                try:
                    value = it.send(sent)
                except StopIteration:
                    break
    

    我还利用generator.next()generator.send(None) 的道德等价这一事实来删除另一个测试。

    EXPR替换为rawStream,将整体包装成一个函数,就可以双向监控数据流向了。

    【讨论】:

    • Martijn,您的贡献非常有帮助,经过充分研究,并且能够复制粘贴到我的代码中以使其正常工作。看到原始参考代码我很害怕,您的简化非常受欢迎。不能要求更多。我发现我不需要将我的 rawStream 包装在 iter() 中,因为它已经提供了一个迭代器,所以这可能节省了几个周期。
    猜你喜欢
    • 1970-01-01
    • 2013-07-03
    • 1970-01-01
    • 2016-09-08
    • 1970-01-01
    • 2011-11-05
    • 1970-01-01
    • 2017-10-29
    • 1970-01-01
    相关资源
    最近更新 更多