Python 深入理解yield


Posted in Python onSeptember 06, 2008

只是粗略的知道yield可以用来为一个函数返回值塞数据,比如下面的例子:

def addlist(alist):
    
for i in alist:
        
yield i + 1
取出alist的每一项,然后把i + 1塞进去。然后通过调用取出每一项:
alist = [1234]
for x in addlist(alist):
    
print x,
这的确是yield应用的一个例子,但是,看过limodou的文章《2.5版yield之学习心得》,并自己反复体验后,对yield有了一个全新的理解。

1. 包含yield的函数

假如你看到某个函数包含了yield,这意味着这个函数已经是一个Generator,它的执行会和其他普通的函数有很多不同。比如下面的简单的函数:
def h():
    
print 'To be brave'
    
yield 5

h()

可以看到,调用h()之后,print 语句并没有执行!这就是yield,那么,如何让print 语句执行呢?这就是后面要讨论的问题,通过后面的讨论和学习,就会明白yield的工作原理了。

2. yield是一个表达式

Python2.5以前,yield是一个语句,但现在2.5中,yield是一个表达式(Expression),比如:
= yield 5
表达式(yield 5)的返回值将赋值给m,所以,认为 m = 5 是错误的。那么如何获取(yield 5)的返回值呢?需要用到后面要介绍的send(msg)方法。

3. 透过next()语句看原理

现在,我们来揭晓yield的工作原理。我们知道,我们上面的h()被调用后并没有执行,因为它有yield表达式,因此,我们通过next()语句让它执行。next()语句将恢复Generator执行,并直到下一个yield表达式处。比如:
def h():
    
print 'Wen Chuan'
    
yield 5
    
print 'Fighting!'

= h()
c.next()
c.next()调用后,h()开始执行,直到遇到yield 5,因此输出结果:
Wen Chuan
当我们再次调用c.next()时,会继续执行,直到找到下一个yield表达式。由于后面没有yield了,因此会??出异常:
Wen Chuan
Fighting!
Traceback (most recent call last):
  File 
"/home/evergreen/Codes/yidld.py", line 11in <module>
    c.next()
StopIteration

4. send(msg) 与 next()

了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做
c.next() 和 c.send(None) 作用是一样的。
来看这个例子:
def h():
    
print 'Wen Chuan',
    m 
= yield 5  # Fighting!
    print m
    d 
= yield 12
    
print 'We are together!'

= h()
c.next()  
#相当于c.send(None)
c.send('Fighting!')  #(yield 5)表达式被赋予了'Fighting!'
输出的结果为:
Wen Chuan Fighting!
需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有yield语句来接收这个值。

5. send(msg) 与 next()的返回值

send(msg) 和 next()是有返回值的,它们的返回值很特殊,返回的是下一个yield表达式的参数。比如yield 5,则返回 5 。到这里,是不是明白了一些什么东西?本文第一个例子中,通过for i in alist 遍历 Generator,其实是每次都调用了alist.Next(),而每次alist.Next()的返回值正是yield的参数,即我们开始认为被压进去的东东。我们再延续上面的例子:
def h():
    
print 'Wen Chuan',
    m 
= yield 5  # Fighting!
    print m
    d 
= yield 12
    
print 'We are together!'

= h()
= c.next()  #m 获取了yield 5 的参数值 5
= c.send('Fighting!')  #d 获取了yield 12 的参数值12
print 'We will never forget the date', m, '.', d
输出结果:
Wen Chuan Fighting!
We will never forget the date 5 . 12

6. throw() 与 close()中断 Generator

中断Generator是一个非常灵活的技巧,可以通过throw抛出一个GeneratorExit异常来终止Generator。Close()方法作用是一样的,其实内部它是调用了throw(GeneratorExit)的。我们看:
def close(self):
    
try:
        self.throw(GeneratorExit)
    
except (GeneratorExit, StopIteration):
        
pass
    
else:
        
raise RuntimeError("generator ignored GeneratorExit")
# Other exceptions are not caught
因此,当我们调用了close()方法后,再调用next()或是send(msg)的话会抛出一个异常:
Traceback (most recent call last):
  File 
"/home/evergreen/Codes/yidld.py", line 14in <module>
    d 
= c.send('Fighting!')  #d 获取了yield 12 的参数值12
StopIteration

注:以上观点属于本人的个人理解,如有偏差请批评指正。谢谢!
Python 相关文章推荐
python正则匹配抓取豆瓣电影链接和评论代码分享
Dec 27 Python
利用Python实现图书超期提醒
Aug 02 Python
python Flask实现restful api service
Dec 04 Python
python实现简易内存监控
Jun 21 Python
Python正则表达式和re库知识点总结
Feb 11 Python
Python获取时间范围内日期列表和周列表的函数
Aug 05 Python
Python使用字典实现的简单记事本功能示例
Aug 15 Python
python破解bilibili滑动验证码登录功能
Sep 11 Python
Python字节单位转换实例
Dec 05 Python
Python drop方法删除列之inplace参数实例
Jun 27 Python
python 使用tkinter+you-get实现视频下载器
Nov 17 Python
Python爬虫进阶之Beautiful Soup库详解
Apr 29 Python
Python 初始化多维数组代码
Sep 06 #Python
Python enumerate遍历数组示例应用
Sep 06 #Python
Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
Sep 06 #Python
Python httplib,smtplib使用方法
Sep 06 #Python
Python 命令行参数sys.argv
Sep 06 #Python
Python 时间处理datetime实例
Sep 06 #Python
Python linecache.getline()读取文件中特定一行的脚本
Sep 06 #Python
You might like
php防注
2007/01/15 PHP
使用Limit参数优化MySQL查询的方法
2008/11/12 PHP
php strcmp使用说明
2010/04/22 PHP
基于php下载文件的详解
2013/06/02 PHP
了解PHP的返回引用和局部静态变量
2015/06/04 PHP
基于OpenCart 开发支付宝,财付通,微信支付参数错误问题
2015/10/01 PHP
PHP编写文件多服务器同步程序
2016/07/02 PHP
windows系统php环境安装swoole具体步骤
2021/03/04 PHP
Mootools 1.2教程 输入过滤第二部分(字符串)
2009/09/15 Javascript
Javascript计算时间差的函数分享
2011/07/04 Javascript
JavaScript简单实现鼠标拖动选择功能
2014/03/06 Javascript
使用JavaScript 实现的人脸检测
2015/03/24 Javascript
js匿名函数作为函数参数详解
2016/06/01 Javascript
fullCalendar中文API官方文档
2017/02/07 Javascript
jQuery dateRangePicker插件使用方法详解
2017/07/28 jQuery
详解EasyUi控件中的Datagrid
2017/08/23 Javascript
微信小程序实现蒙版弹窗效果
2018/11/01 Javascript
详解在Node.js中发起HTTP请求的5种方法
2019/01/10 Javascript
更强大的vue ssr实现预取数据的方式
2019/07/19 Javascript
layui 地区三级联动 form select 渲染的实例
2019/09/27 Javascript
解决vue打包 npm run build-test突然不动了的问题
2020/11/13 Javascript
最近Python有点火? 给你7个学习它的理由!
2017/06/26 Python
django2 快速安装指南分享
2018/01/05 Python
Python while循环使用else语句代码实例
2020/02/07 Python
深入了解python列表(LIST)
2020/06/08 Python
canvas实现图片镜像翻转的2种方式
2020/07/22 HTML / CSS
历史专业毕业生的自我鉴定
2013/11/15 职场文书
致接力运动员广播稿
2014/02/17 职场文书
优秀应届毕业生推荐信
2014/02/18 职场文书
数控专业自荐书范文
2014/03/16 职场文书
农村文化活动总结
2014/08/28 职场文书
党员查摆问题及整改措施
2014/10/10 职场文书
借条格式范本
2015/05/25 职场文书
2015年教师节主持词
2015/07/03 职场文书
python单元测试之pytest的使用
2021/06/07 Python
MySQL 原理与优化之原数据锁的应用
2022/08/14 MySQL