基于收益率的相当于Python3的收益来自'委托而不会丢失发送

cef*_*efn 2 python yield delegation generator send

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

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

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

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

byte = stream.send(True)

byte = next(stream)
Run Code Online (Sandbox Code Playgroud)

...此特殊调用从生成器获取一个字节而不增加流中的位置.

byte = stream.send(False)
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.我的低内存JSON解析器(https://github.com/ShrimpingIt/medea)运行良好.https://github.com/ShrimpingIt/medea/blob/dd0007e657cd487913c72993dcdaf0f60d8ee30e/examples/scripts/twitterValuesNamed.py上的示例能够处理来自文件的缓存推文.

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

出于这个原因,我首先创建HTTPS流,然后从流中读取字节,处理内容长度标头并跳转到HTTP标头末尾的"\ r \n\r \n",然后将流处理到正确的位置到解析器.此时我知道流在停止之前应该服务多少内容字节.

不幸的是,我遇到了语法,表达或理解问题.

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

def delegatingStream():
    yield from rawStream
Run Code Online (Sandbox Code Playgroud)

但是,我无法想象如何创建一个迭代器,智能地使用contentLength值在contentLength之后终止而不破坏send().

我需要使用yield能够干预迭代器逻辑,但yield from似乎只允许委派send().我甚至无法一起发送和收集来复制delegatingStream()的行为.例如,这不具有相同的效果.

def relayingStream():
    while True:
        yield rawStream.send((yield))
Run Code Online (Sandbox Code Playgroud)

避免的原因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)
Run Code Online (Sandbox Code Playgroud)

知道如何正确转发值send(),而不使用yield from

Mar*_*ers 5

您可以在Formal Semantics部分中查阅PEP 380 - 委托给子生成器的语法,以查看yield fromPython等效项.

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
Run Code Online (Sandbox Code Playgroud)

您可以完全实现替换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
Run Code Online (Sandbox Code Playgroud)

我还使用了generator.next()道德等同generator.send(None)于移除另一个测试的事实.

更换EXPRrawStream和整个包装成一个功能,可以监视数据在两个方向流动.