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文件夹与文件的操作实现代码
Jul 13 Python
Python生成验证码实例
Aug 21 Python
Python实现数通设备端口使用情况监控实例
Jul 15 Python
python+Django+apache的配置方法详解
Jun 01 Python
python 专题九 Mysql数据库编程基础知识
Mar 16 Python
python tensorflow学习之识别单张图片的实现的示例
Feb 09 Python
小白入门篇使用Python搭建点击率预估模型
Oct 12 Python
python异常触发及自定义异常类解析
Aug 06 Python
浅谈pytorch、cuda、python的版本对齐问题
Jan 15 Python
利用 Python ElementTree 生成 xml的实例
Mar 06 Python
django 取消csrf限制的实例
Mar 13 Python
python操作ini类型配置文件的实例教程
Oct 30 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
LotusPhp笔记之:Logger组件的使用方法
2013/05/06 PHP
php漏洞之跨网站请求伪造与防止伪造方法
2013/08/15 PHP
php根据数据id自动生成编号的实现方法
2016/10/16 PHP
浅析PHP开发规范
2018/02/05 PHP
阿里云Win2016安装Apache和PHP环境图文教程
2018/03/11 PHP
Yii2框架视图(View)操作及Layout的使用方法分析
2019/05/27 PHP
greybox——不开新窗口看新的网页
2007/02/20 Javascript
jQuery编辑器KindEditor4.1.4代码高亮显示设置教程
2013/03/01 Javascript
用jquery中插件dialog实现弹框效果实例代码
2013/11/15 Javascript
jquery显示隐藏input对象
2014/07/21 Javascript
jquery中ajax使用error调试错误的方法
2015/02/08 Javascript
JS实现点击按钮后框架内载入不同网页的方法
2015/05/05 Javascript
在html中引入外部js文件,并调用带参函数的方法
2016/10/31 Javascript
JS求解三元一次方程组值的方法
2017/01/03 Javascript
详解使用nvm管理多版本node的方法
2017/08/30 Javascript
浅谈angular4.0中路由传递参数、获取参数最nice的写法
2018/03/12 Javascript
Smartour 让网页导览变得更简单(推荐)
2019/07/19 Javascript
Openlayers实现点闪烁扩散效果
2020/09/24 Javascript
Python写的一个简单DNS服务器实例
2014/06/04 Python
python版微信跳一跳游戏辅助
2018/01/11 Python
解决phantomjs截图失败,phantom.exit位置的问题
2018/05/17 Python
python语音识别实践之百度语音API
2018/08/30 Python
详解分布式任务队列Celery使用说明
2018/11/29 Python
详解用python写一个抽奖程序
2019/05/10 Python
详解Python 多线程 Timer定时器/延迟执行、Event事件
2019/06/27 Python
PyCharm中代码字体大小调整方法
2019/07/29 Python
基于python计算并显示日间、星期客流高峰
2020/05/07 Python
纯CSS3制作的简洁蓝白风格的登录模板(非IE效果更好)
2013/08/11 HTML / CSS
酒店管理专业毕业生求职自荐信
2014/04/28 职场文书
普通党员自我剖析材料
2014/10/07 职场文书
个人自查自纠材料
2014/10/14 职场文书
教师聘用意向书
2015/05/11 职场文书
最感人的道歉情书
2015/05/12 职场文书
工作收入证明模板
2015/06/12 职场文书
mysql 联合索引生效的条件及索引失效的条件
2021/11/20 MySQL
Android在Sqlite3中的应用及多线程使用数据库的建议
2022/04/24 Java/Android