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的方式来调用子生成器。

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

《翻译》PEP 380 – 委托子生成器语法的更多相关文章

  1. Python PEP 492 中文翻译——协程与async/await语法

    原文标题:PEP 0492 -- Coroutines with async and await syntax 原文链接:https://www.python.org/dev/peps/pep-049 ...

  2. [译]PEP 342--增强型生成器:协程

    PEP原文 : https://www.python.org/dev/peps/pep-0342/ PEP标题: Coroutines via Enhanced Generators PEP作者: G ...

  3. [译] PEP 255--简单的生成器

    我正打算写写 Python 的生成器,然而查资料时发现,引入生成器的 PEP 没人翻译过,因此就花了点时间翻译出来.如果在阅读时,你有读不懂的地方,不用怀疑,极有可能是我译得不到位.若出现这种情况,我 ...

  4. PHP 生成器语法

    一般你在迭代一组数据的时候,需要创建一个数据,假设数组很大,则会消耗很大性能,甚至造成内存不足. //Fatal error: Allowed memory size of 1073741824 by ...

  5. Python异步IO之协程(一):从yield from到async的使用

    引言:协程(coroutine)是Python中一直较为难理解的知识,但其在多任务协作中体现的效率又极为的突出.众所周知,Python中执行多任务还可以通过多进程或一个进程中的多线程来执行,但两者之中 ...

  6. Python用yield form 实现异步协程爬虫

    很古老的用法了,现在大多用的aiohttp库实现,这篇记录仅仅用做个人的协程底层实现的学习. 争取用看得懂的字来描述问题. 1.什么是yield 如果还没有怎么用过的话,直接把yield看做成一种特殊 ...

  7. [译]PEP 380--子生成器的语法

    导语: PEP(Python增强提案)几乎是 Python 社区中最重要的文档,它们提供了公告信息.指导流程.新功能的设计及使用说明等内容.对于学习者来说,PEP 是非常值得一读的第一手材料,学习中遇 ...

  8. PEP 492 -- Coroutines with async and await syntax 翻译

    因为工作中慢慢开始用python的协程,所以想更好的理解一下实现方式,故翻译此文 原文中把词汇表放到最后,但是我个人觉得放在最开始比较好,这样可以增加当你看原文时的理解程度 词汇表 原生协程函数 Na ...

  9. python中的PEP是什么?怎么理解?(转)

    PEP是什么? PEP的全称是Python Enhancement Proposals,其中Enhancement是增强改进的意思,Proposals则可译为提案或建议书,所以合起来,比较常见的翻译是 ...

随机推荐

  1. supervisor管理进程 superlance对进程状态报警

    supervisor介绍 首先,介绍一下supervisor.Supervisor(http://supervisord.org/)是用Python开发的一个client/server服务,是Linu ...

  2. win10 添加项目右键用vscode打开

    1.新建reg文件:在vscode安装目录下新建一个文本文件,然后将文件后缀改为:*.reg,文件名任意,例如:vsCodeOpenFolder.reg. 2.编写文本文件内容.将下面的内容Copy到 ...

  3. 《linux内核分析》作业一:分析汇编代码

    通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的(王海宁) 姓名:王海宁                             学号:20135103 课程:<Linux内核分析& ...

  4. OVS 精确删除流表

    OVS 精确删除流表 前言 今天看了sdnlab小姐姐的mininet教程之后,看到有一个下流表的操作,优先级没能下成功,然后回来实验一下,这个问题是解决了,不过遇到了一个小问题,ovs如何精确删除流 ...

  5. final发布48小时用户调查报告

    小组名称:飞天小女警 项目名称:礼物挑选小工具 小组成员:沈柏杉(组长).程媛媛.杨钰宁.谭力铭 调查问卷标题:用户调查报告 调查目的:在final版本发布后的用户调查报告 调查问卷的数量:11 问卷 ...

  6. 【转载】Understanding When to use RabbitMQ or Apache Kafka

    https://content.pivotal.io/rabbitmq/understanding-when-to-use-rabbitmq-or-apache-kafka RabbitMQ: Erl ...

  7. Linux命令(四)删除文件 rm

    用户可以使用 rm 命令删除不需要的文件. rm 可以删除文件或目录,并且支持通配符. 如果目录中存在其它文件则会递归删除. 删除软链接只是删除链接,对应的文件或目录不会被删除. 软链接类似于 win ...

  8. information_schema系列十一

    1: INNODB_CMP 和INNODB_CMP_RESET 这两个表存储的是关于压缩INNODB信息表的时候的相关信息, Column name Description PAGE_SIZE Com ...

  9. BZOJ4654 NOI2016国王饮水记(动态规划+三分)

    有很多比较显然的性质.首先每个城市(除1外)至多被连通一次,否则没有意义.其次将城市按水位从大到小排序后,用以连通的城市集合是一段前缀,并且不应存在比1城市还小的.然后如果确定了选取的城市集合,每次选 ...

  10. Python:目录和文件的操作模块os.path和OS常用方法

    1.目录和文件的操作模块os.path,在使用之前要先导入:import os.path.它主要有以下几个重要的功能函数: #!/user/bin/python #coding= utf-8 impo ...