深入学习python的yield和generator


Posted in Python onMarch 10, 2016

前言
没有用过的东西,没有深刻理解的东西很难说自己会,而且被别人一问必然破绽百出。虽然之前有接触过python协程的概念,但是只是走马观花,这两天的一次交谈中,别人问到了协程,顿时语塞,死活想不起来曾经看过的东西,之后突然想到了yield,但为时已晚,只能说概念不清,所以本篇先缕缕python的生成器和yield关键字。

什么是生成器
1、生成器是一个特殊的程序,可以被用作控制循环的迭代行为
2、生成器类似于返回值为数组的一个函数,这个函数可以接收参数,可以被调用,但是,不同于一般的函数会一次性返回包含了所有数值的数组,生成器一次只产生一个值,这样消耗的内粗数量大大减少,而且允许调用函数可以很快的开始处理前几个返回值。因此,生成器看起来像一个函数但是表现的却像一个迭代器。
python中的生成器
python提供了两种基本的方式。

1)、生成器函数:也是用def来定义,利用关键字yield一次返回一个结果,阻塞,重新开始
2)、生成器表达式:返回一个对象,这个对象只有在需要的时候才产生结果
下面详细讲解。

1、生成器函数
为什么叫生成器函数?因为他随着时间的推移生成了一个数值队列。一般的函数在执行完毕之后会返回一个值然后退出,但是生成器函数会自动挂起,然后重新拾起继续执行,他会利用yield关键字关起函数,给调用者返回一个值,同时保留了当前的足够多的状态,可以使函数继续执行。生成器和迭代协议是密切相关的,可迭代的对象都有一个__next()__成员方法,这个方法要么返回迭代的下一项,要么引起异常结束迭代。
为了支持迭代协议,拥有yield语句的函数被编译为生成器,这类函数被调用时返回一个生成器对象,返回的对象支持迭代接口,即成员方法__next()__继续从中断处执行执行。
看下面的例子:

# codes
def create_counter(n):
 print "create counter"
 while True:
  yield n
  print 'increment n'
  n += 1

cnt = create_counter(2)
print cnt
print next(cnt)
print next(cnt)

# output
<generator object create_counter at 0x0000000001D141B0>
create counter
2
increment n
3

分析一下这个例子:

  • 在create_counter函数中出现了关键字yield,预示着这个函数每次只产生一个结果值,这个函数返回一个生成器(通过第一行输出可以看出来),用来产生连续的n值
  • 在创造生成器实例的时候,只需要像普通函数一样调用就可以,但是这个调用却不会执行这个函数,这个可以通过输出看出来
  • next()函数将生成器对象作为自己的参数,在第一次调用的时候,他执行了create_counter()函数到yield语句,返回产生的值2
  • 我们重复的调用next()函数,每次他都会从上次被挂起的地方开始执行,直到再次遇到了yield关键字

为了更加深刻的理解,我们再举一个例子。

#coding
def cube(n):
 for i in range(n):
  yield i ** 3

for i in cube(5):
 print i

#output
0
1
8
27
64

所以从理解函数的角度出发我们可以将yield类比为return,但是功能确实完全不同,在for循环中,会自动遵循迭代规则,每次调用next()函数,所以上面的结果不难理解。

2、生成器表达式
生成器表达式来自于迭代和列表解析的组合,关于列表解析的概念和用法可以参见我之前的博客,生成器表达式和列表解析类似,但是他使用尖括号而不是方括号括起来的。如下代码:

>>> # 列表解析生成列表
>>> [ x ** 3 for x in range(5)]
[0, 1, 8, 27, 64]
>>> 
>>> # 生成器表达式
>>> (x ** 3 for x in range(5))
<generator object <genexpr> at 0x000000000315F678>
>>> # 两者之间转换
>>> list(x ** 3 for x in range(5))
[0, 1, 8, 27, 64]

就操作而言,生成器表如果使用大量的next()函数会显得十分不方便,for循环会自动出发next函数,所以可以按下面方式使用:

>>> for n in (x ** 3 for x in range(5)):
 print('%s, %s' % (n, n * n))

 
0, 0
1, 1
8, 64
27, 729
64, 4096
>>>

两者比较
一个迭代既可以被写成生成器函数,也可以被协程生成器表达式,均支持自动和手动迭代。而且这些生成器只支持一个active迭代,也就是说生成器的迭代器就是生成器本身。

总结
想起了初中时候老师经常说的,眼观千遍,不如手动一遍。

Python 相关文章推荐
python实现探测socket和web服务示例
Mar 28 Python
Python Property属性的2种用法
Jun 21 Python
python logging 日志轮转文件不删除问题的解决方法
Aug 02 Python
Python 使用requests模块发送GET和POST请求的实现代码
Sep 21 Python
Python 操作MySQL详解及实例
Apr 30 Python
Python 创建空的list,以及append用法讲解
May 04 Python
python celery分布式任务队列的使用详解
Jul 08 Python
python圣诞树编写实例详解
Feb 13 Python
Python GUI编程学习笔记之tkinter控件的介绍及基本使用方法详解
Mar 30 Python
Python 解决相对路径问题:&quot;No such file or directory&quot;
Jun 05 Python
关于python中导入文件到list的问题
Oct 31 Python
python爬虫线程池案例详解(梨视频短视频爬取)
Feb 20 Python
Python中random模块生成随机数详解
Mar 10 #Python
python生成器表达式和列表解析
Mar 10 #Python
python迭代器与生成器详解
Mar 10 #Python
Python装饰器基础详解
Mar 09 #Python
Python求算数平方根和约数的方法汇总
Mar 09 #Python
Python实现Linux命令xxd -i功能
Mar 06 #Python
基于Python实现一个简单的银行转账操作
Mar 06 #Python
You might like
php使用unset()删除数组中某个单元(键)的方法
2015/02/17 PHP
php基于session实现数据库交互的类实例
2015/08/03 PHP
thinkPHP基于ajax实现的菜单与分页示例
2016/07/12 PHP
php中类和对象:静态属性、静态方法
2017/04/09 PHP
Yii框架数据库查询、增加、删除操作示例
2019/10/14 PHP
javascript AutoScroller 函数类
2009/05/29 Javascript
简单的jquery拖拽排序效果实现代码
2011/09/20 Javascript
setInterval,setTimeout与jquery混用的问题
2013/04/08 Javascript
js AppendChild与insertBefore用法详细对比
2013/12/16 Javascript
js ajaxfileupload.js上传报错的解决方法
2016/05/05 Javascript
js实现带三角符的手风琴效果
2017/03/01 Javascript
vue2.0中goods选购栏滚动算法的实现代码
2017/05/17 Javascript
详解如何让InstantClick兼容MathJax、百度统计等
2017/09/12 Javascript
通过js控制时间,一秒一秒自己动的实例
2017/10/25 Javascript
JS+WCF实现进度条实时监测数据加载量的方法详解
2017/12/19 Javascript
vue实现在表格里,取每行的id的方法
2018/03/09 Javascript
Angular学习笔记之集成三方UI框架、控件的示例
2018/03/23 Javascript
VUE中使用MUI方法
2019/02/12 Javascript
生产制造追溯系统之再说条码打印
2019/06/03 Javascript
vue实现自定义H5视频播放器的方法步骤
2019/07/01 Javascript
jQuery实现简单弹幕效果
2019/11/28 jQuery
Vue中添加滚动事件设置的方法详解
2020/09/14 Javascript
[43:03]LGD vs Newbee 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/19 DOTA
使用Nginx+uWsgi实现Python的Django框架站点动静分离
2016/03/21 Python
Django接受前端数据的几种方法总结
2016/11/04 Python
python 将print输出的内容保存到txt文件中
2018/07/17 Python
Python 数值区间处理_对interval 库的快速入门详解
2018/11/16 Python
python自动生成model文件过程详解
2019/11/02 Python
python实现名片管理器的示例代码
2019/12/17 Python
html5 Canvas画图教程(11)—使用lineTo/arc/bezierCurveTo画椭圆形
2013/01/09 HTML / CSS
What's the difference between deep copy and shallow copy? (深拷贝与浅拷贝有什么区别)
2015/11/10 面试题
品恩科技软件测试面试题
2014/10/26 面试题
收款委托书
2014/10/14 职场文书
撤诉状格式范本
2015/05/19 职场文书
宪法宣传标语100条
2019/10/15 职场文书
Android Flutter实现3D动画效果示例详解
2022/04/07 Java/Android