Python: generator, yield, yield from 详解
1.Generator Expressions
生成器表达式是用小括号表示的简单生成器标记法:
generator_expression ::= "(" expression comp_for ")"
生成器表达式产生一个生成器对象,它的语法和for类似,出了它是被“()”包含,而不是[]或{};
生成器表达式中变量的计算被延迟到__next__()函数的调用,然而最左边for循环子句被立即计算,这样,如果他有错误的话可以被立即看到。后面的for循环子句不能被立即计算,因为他们可能依赖于前面的for循环,例如(x*y for x in range(10) for y in bar(x))
python3.6以后,如果生成器出现在async def function中,那么async for子句和await表达式可以被理解为是异步的。如果生成器表达式包含async for子句或者await表达式,就叫做异步生成器表达式。异步生成器表达式产生一个新的异步生成器对象,它是一个异步迭代器。
2. Yield Expressions
yield_atom ::= "(" yield_expression ")"
yield_expression ::= "yield" [expression_list | "from" expression]
Yield表达式用于定义一个生成器函数或异步生成器函数,因此只能被用于一个函数定义体内。在一个函数定义体中使用yield表达式使其成为生成器,
在一个async def函数体内使用yield表达式使协程函数成为一个异步生成器。例如:
def gen(): //定义一个生成器函数
yield 123
async def agen(): //定义一个异步生成器函数
yield 123
当一个生成器函数被调用,它返回一个迭代器,也叫生成器。这个生成器控制生成器函数的执行。当生成器的一个函数被调用的时候,生成器函数开始执行。
执行到第一个yield表达式时,挂起,返回expression_list的值给调用者。对于挂起,我们指的是所有局部状态被保留,包含当前局部变量的绑定,
指令指针,内部调用栈,任何异常处理状态。当通过调用生成器的一个函数恢复执行时,函数的执行就好像是从外部再次调用yield表达式一样。恢复执行后,
yield表达式的值取决调用的方法。如果是__next__()被调用(一般通过for循环或者内置的next()函数),那么值是None。如果是send()被调用,
值是传给send的参数的值。
所有的这些使得生成器函数非常像协程;它产生多次值,有多个入口点并且执行可以被挂起。唯一的不同是,生成器函数yield后不能控制程序从哪里继续执行,
控制权总是传回给生成器的调用者。
yield表达式可以在try块的任何地方,如果生成器在被结束(到达零引用或者因为垃圾回收机制)之前没有被恢复执行,生成器的close函数被调用,因此finally
子句被执行。
当yield from <expr> 被使用,它把附加的表达式当成一个子迭代器,所有子迭代器产生的值被直接传回给当前生成器函数的调用者。当前生成器调用send的参数值
和调用throw的异常参数 都将被传给底层迭代器(子迭代器),如果他有对应的方法的话。否则,send导致AttributeError或者TypeError,而throw立即
raise传给他的异常。
当底层迭代器执行完成,StopIteration对象的value值变成这个yield from表达式的值。这个值可以被显式的设置当产生StopIteration时,或者自动设置,如果
子迭代器是一个生成器(子生成器返回一个值)
3. 生成器-迭代器 方法
这部分介绍生成器迭代器的方法,他们可以被用来控制生成器函数的执行。当生成器正在执行时调用这些函数将导致ValueError。
(1)generator.__next__()
开始生成器函数的执行或者从最后被执行的yield表达式中恢复执行,如果是恢复执行,yield表达式的值是None,继续执行到下一个yield表达式,
挂起,返回expression_list的值给__next__()的调用者。如果生成器没有再yield一个值,则产生一个StopIteration异常
这个方法一般被隐式调用,例如for循环,next()
(2)generator.send(value)
恢复执行并且发送一个值到生成器函数。这个value参数就是当前yield表达式的结果。send方法返回下一个生成器yield的值
如果没有yield,返回StopIteration。当用send来启动生成器,他的参数必须是None,因为没有yield表达式接收值。
(3)generator.throw(type[,value[,traceback]])
在生成器挂起的地方产生一个type类型的异常,并且返回下一个生成器函数yield的值,如果没有yield,返回StopIteration。
如果生成器函数没有捕获这个传进去的异常 ,或者产生了另一个不同的异常,那么将这个异常传递给调用者。
(4)generator.close()
在生成器挂起的地方产生一个GeneratorExit(),如果生成器之后优雅的退出,已经关闭,或者产生了一个GeneartorExit(不捕获该异常),close将返回到它的
调用者。如果生成器yield一个值,那么产生一个RuntimeError。如果生成器产生了任何其他异常,将传递给调用者。如果
生成器因为一个异常或者正常退出,那么close不做任何事情。
实例:
>>> def echo(value=None):
... print("Execution starts when 'next()' is called for the first time.")
... try:
... while True:
... try:
... value = (yield value)
... except Exception as e:
... value = e
... finally:
... print("Don't forget to clean up when 'close()' is called.")
...
>>> generator = echo(1)
>>> print(next(generator))
Execution starts when 'next()' is called for the first time.
1
>>> print(next(generator))
None
>>> print(generator.send(2))
2
>>> generator.throw(TypeError, "spam")
TypeError('spam',)
>>> generator.close()
Don't forget to clean up when 'close()' is called. PEP380 加入了yield from表达式,允许一个生成器委派部分操作给另一个生成器。这可以剔除一部分包含yield的代码放到另一个生成器。另外,
子生成器可以返回一个值,这个值对于委托生成器也是可用的。
虽然主要涉及用来委派一个子生成器,但是yield from 表达式事实上可以委派任何的子迭代器。
低于简单的迭代器, yield from iterable 本质上就是一个简短的形式:for item in iterable: yield item,例如:
>>> def g(x):
... yield from range(x, 0, -1)
... yield from range(x)
...
>>> list(g(5))
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]
然而,不像普通的循环,yield from 允许子生成器直接从调用区域接收send和throw的值,并且返回一个最后的值给外层生成器。示例如下:
>>> def accumulate():
... tally = 0
... while 1:
... next = yield
... if next is None:
... return tally
... tally += next
...
>>> def gather_tallies(tallies):
... while 1:
... tally = yield from accumulate()
... tallies.append(tally)
...
>>> tallies = []
>>> acc = gather_tallies(tallies)
>>> next(acc) # Ensure the accumulator is ready to accept values
>>> for i in range(4):
... acc.send(i)
...
>>> acc.send(None) # Finish the first tally
>>> for i in range(5):
... acc.send(i)
...
>>> acc.send(None) # Finish the second tally
>>> tallies
[6, 10]
Python: generator, yield, yield from 详解的更多相关文章
- Python Deque 模块使用详解,python中yield的用法详解
Deque模块是Python标准库collections中的一项. 它提供了两端都可以操作的序列, 这意味着, 你可以在序列前后都执行添加或删除. https://blog.csdn.net/qq_3 ...
- [转]使用python来操作redis用法详解
转自:使用python来操作redis用法详解 class CommRedisBase(): def __init__(self): REDIS_CONF = {} connection_pool = ...
- Python安装、配置图文详解(转载)
Python安装.配置图文详解 目录: 一. Python简介 二. 安装python 1. 在windows下安装 2. 在Linux下安装 三. 在windows下配置python集成开发环境(I ...
- 【和我一起学python吧】Python安装、配置图文详解
Python安装.配置图文详解 目录: 一. Python简介 二. 安装python 1. 在windows下安装 2. 在Linux下安装 三. 在windows下配置python集成开发环境( ...
- Python中的高级数据结构详解
这篇文章主要介绍了Python中的高级数据结构详解,本文讲解了Collection.Array.Heapq.Bisect.Weakref.Copy以及Pprint这些数据结构的用法,需要的朋友可以参考 ...
- Python中格式化format()方法详解
Python中格式化format()方法详解 Python中格式化输出字符串使用format()函数, 字符串即类, 可以使用方法; Python是完全面向对象的语言, 任何东西都是对象; 字符串的参 ...
- python设计模式之装饰器详解(三)
python的装饰器使用是python语言一个非常重要的部分,装饰器是程序设计模式中装饰模式的具体化,python提供了特殊的语法糖可以非常方便的实现装饰模式. 系列文章 python设计模式之单例模 ...
- Python调用windows下DLL详解
Python调用windows下DLL详解 - ctypes库的使用 2014年09月05日 16:05:44 阅读数:6942 在python中某些时候需要C做效率上的补充,在实际应用中,需要做部分 ...
- Python操作redis字符串(String)详解 (三)
# -*- coding: utf-8 -*- import redis #这个redis不能用,请根据自己的需要修改 r =redis.Redis(host=") 1.SET 命令用于设置 ...
- 【Python】Python内置函数dir详解
1.命令介绍 最近学习并使用了一个python的内置函数dir,首先help一下: 复制代码代码如下: >>> help(dir)Help on built-in function ...
随机推荐
- 深入解析alloc/retain/release/dealloc实现
首先通过GNUstep上得源码来叙述各个函数的实现(GNUstep是Cocoa框架的互换框架,二者的行为和实现方式很相似) GNUstep源码中NSObject类的alloc方法: id = obj ...
- JavaScript历史状态管理
1.API (1)history.pushState:能够在不加载新页面的情况下改变浏览器的 URL history.pushState({name:"Nicholas"}, &q ...
- Nginx:解析HTTP配置的流程
参考资料:深入理解Nginx(陶辉) 书中有详细的讲解,这里只用本人的理解梳理一下该流程. 一点提议:对于像我这样的新手,面对暂时看不懂章节,建议先往下看一下(可能就会有新的理解或灵感),而不要死磕在 ...
- 把质量控制工作往前推进(1)——安装sonarqube
曾经关注点一直在怎么提高应用程序的质量,没太在意代码级别的质量.近期由于某些因素的推动,须要关注到代码级别的质量去,把质量工作尽量往前推,也符合质量控制的原则. 试用了一下sonarqube(老版本 ...
- 不得不看的Java代码性能优化总结
原文:https://blog.csdn.net/mr_smile2014/article/details/50112723 前言 代码优化,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么 ...
- excel表格快捷键
CTRL+A 全选 CTRL+B 加粗 CTRL+C 复制 CTRL+D 下拉(复制上一个单元格的格式和内容) CTRL+G 定位 CTRL+F ...
- <!DOCTYPE>奇葩的问题
作用:<!DOCTYPE> 声明不是 HTML 标签:它是指示 web 浏览器关于页面使用哪个 HTML 版本号进行编写的指令. 1.:<!DOCTYPE> 声明没有结束标签. ...
- [译]NeHe教程 - 添加颜色
原文: Adding Colour 上一节我讲解了如何在屏幕显示三角形和四边形.本节会讲解如何上色.单色(Flat)顾名思义就是只能涂一种实心的颜色.平滑颜色(Smooth)可以在各个顶点混合三种颜色 ...
- 篇三、开发前知识补充:Android的长度单位和屏幕分辨率,这个也是转载~~
这篇文章有点早,不过很实用.单位的实用看最后的红色标注的部分. 屏幕分辨率基础 1.术语和概念 术语 说明 备注 Screen size(屏幕尺寸) 指的是手机实际的物理尺寸,比如常用的2.8英寸,3 ...
- 【Mac + Mysql + Navicat Premium】之Navicat Premium如何连接Mysql数据库
参考文章: <mac用brew安装mysql,设置初始密码> 因为我需要连接数据库工具,需要密码,所以下面介绍如何设置.修改密码实现Navicat Premium连接Mysql数据库 建议 ...