• 《翻译》PEP 380 – 委托子生成器语法


     PEP 380 – 委托子生成器语法

     翻译自: https://www.python.org/dev/peps/pep-0380/

    摘要

    一项新的语法被提出了:生成器委托其部分操作给另一个生成器。委托也就意味着包含’yield’的那部分代码可能被分解,并且放置在另一个生成器里。此外,子生成器可以返回一个值,而且这个值对于委托生成器可用(即上层的生成器)。

    同时,当一个生成器重复多次的yield另一个生成器产生的值时,可以通过这项新的语法进行优化。

    PEP授理  Guido 正式接受这项 PEP [1]  于 26日 6月, 2011.

    目的

    Python的生成器是协程的一种表现形式,但它有局限性,只能向它的直接调用者yield。也就是说,包含yield语句的代码片段不能被分解,也不能放在单独的函数里。这么做导致了被调用的函数变成了一个生成器,并且有必要明确地迭代第二个生成器,重新re-yield值。

    如果只考虑yield的值,可以使用这样的循环:

    for v in g:
        yield v

    然而,如果子生成器要适当地与调用者进行交互,比如调用send(),throw()和close()方法时,就会变得相当复杂。稍后会看到,要写的代码相当复杂,处理边界情况也是用尽心机。

    为解决这个问题,一项新的语法被提出来。在最简单的情况下,它的使用等同于for循环,同时它也会处理原有的生成器所有行为,以一种简单直接的方式让生成器代码可以被分解。

    提议

    可以将下列的新语法应用在生成器内:

    yield from <expr>

    其中,<expr>是一个可迭代对象(iterable),可以从中提取出迭代器。这个迭代器会从调用者的生成器yield值和接收值,直到耗尽。

    此外,如果这个迭代器是另一个生成器的话,子生成器可以使用return返回一个值,这个值就作为yield from语句的返回值。

    根据生成器协议,yield from表达式文法可以这样描述:

    • 迭代器yield的任何值都直接传递给调用者
    • 使用send()传递给委托生成器的值,都直接传递到迭代器。如果send()的参数是None,那这个迭代器的__next__()方法会被调用。如果send()的参数不是None,那迭代器的send()方法被调用。如果调用时抛出StopIteration异常,那么委托生成器被恢复。其他任何的异常都会传到委托生成器。
    • 除了GeneratorExit异常外,丢进委托生成器的异常都被传递到了迭代器的throw()方法。如果调用时抛出StopIteration异常,委托生成器被恢复。其他任何的异常都会传到委托生成器。
    • 如果GeneratorExit异常被丢进委托生成器,或者调用了委托生成器的close()方法,那么迭代器的close()方法会被调用。如果调用时发生异常,会传递到委托生成器。否则,GeneratorExit会在委托生成器抛出。
    • yield from表达式的值是传递给StopIteration异常的第一个参数。
    • 在生成器里,return expr 将导致StopIteration(expr)抛出。

    增强StopIteration

    为了方便使用,StopIteration异常将有一个value属性,它的值是创建时传入的第一个参数。

    正式语义

    python 3 语法使用如下.

       1.表达式

    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

       2.在一个生成器内,表达式

    return value

    在语义上等同于

    raise StopIteration(value)

    除了不能被except捕获的异常

       1.StopIteration异常表现如下:

    class StopIteration(Exception):
    def __init__(self, *args):
            if len(args) > 0:
                self.value = args[0]
            else:
               self.value = None
            Exception.__init__(self, *args)

    理论

        重构原则

    上面说的这些语义,目的是能够重构生成器代码。应该让一部分代码包含有一个或多个yield表达式,分到独自的函数里(也就是使用常规的技巧处理作用域的变量引用),最终通过使用yield from表达式来调用新的函数。

    在任何场景下,合成生成器应该与最初的,没分割的生成器行为一致,这就包括__next__(),send(),throw()和close()方法的调用。

    相比于生成器,子迭代器语法是一个更合理,更一般的做法。

    就重构来说,被提议的新语法有下面几条限制:

    • 捕获GeneratorExit异常的代码块如果没有在接下来的代码里重新抛出异常的话,那么这块代码就不能被分割。尽管保留着同样的行为。
    • 如果StopIteration异常被丢进委托生成器,被分割的代码的行为可能与未分割的不一样。

        终结

    关于生成器的终结,有一些争论:在调用close()方法显示终结委托生成器,当它在yield from挂起时,也应该同时终结子生成器。反对的人认为,这会导致子生成器过早的被终结,如果这个子生成器被其他地方引用的话。

    考虑到每个Python的实现版本的差异(非重计数Python),最好还是显示的终结生成器,这也保证了被分割的代码与未分割的代码在Python的各个版本里有同样的行为。

    在大多数的使用场景下,子生成器不会被共用。即使存在少数子生成器共用的情况,子生成器能使用包裹调用throw()和close()的方法容纳,或者使用一种替代yield from的方式来调用子生成器。

    -----未完还在翻译中。。。

  • 相关阅读:
    MyBatis逆向工程自动生成配置文件 spring boot工程
    使用Sencha打包报错Java heap space的解决办法
    Mybatis传递多个参数
    国内有哪些顶级技术团队的博客值得推荐?[转]
    tcp五元组
    linux环境代码不能发送UDP广播包
    SAP ABAP Development Tool 提高开发效率的十个小技巧
    如何在 SAP BTP 上通过 CDS view 快速创建 Fiori Elements 应用
    SAP Spartacus 的延迟加载 Lazy load 设计原理
    写给即将离开校园准备进入 SAP 研究院实习的朋友
  • 原文地址:https://www.cnblogs.com/cool-fire/p/8611380.html
Copyright © 2020-2023  润新知