Python生成器(Generator)详解


Posted in Python onApril 13, 2015

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。

简单生成器

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

>>> L = [x * x for x in range(10)]

>>> L

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> g = (x * x for x in range(10))

>>> g

<generator object <genexpr> at 0x104feab40>

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

如果要一个一个打印出来,可以通过generator的next()方法:

>>> g.next()

0

>>> g.next()

1

>>> g.next()

4

>>> g.next()

9

>>> g.next()

16

>>> g.next()

25

>>> g.next()

36

>>> g.next()

49

>>> g.next()

64

>>> g.next()

81

>>> g.next()

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

StopIteration

我们讲过,generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

当然,上面这种不断调用next()方法实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

>>> g = (x * x for x in range(10))

>>> for n in g:

...     print n

...

0

1

4

9

16

25

36

49

64

81

所以,我们创建了一个generator后,基本上永远不会调用next()方法,而是通过for循环来迭代它。

带yield 语句的生成器

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print b改为yield b就可以了:

def fib(max):

    n, a, b = 0, 0, 1

    while n < max:

        yield b

        a, b = b, a + b

        n = n + 1

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
>>> fib(6)

<generator object fib at 0x104feaaa0>

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

举个简单的例子,定义一个generator,依次返回数字1,3,5:

>>> def odd():

...     print 'step 1'

...     yield 1

...     print 'step 2'

...     yield 3

...     print 'step 3'

...     yield 5

...

>>> o = odd()

>>> o.next()

step 1

1

>>> o.next()

step 2

3

>>> o.next()

step 3

5

>>> o.next()

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

StopIteration

可以看到,odd不是普通函数,而是generator,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next()就报错。

回到fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。

同样的,把函数改成generator后,我们基本上从来不会用next()来调用它,而是直接使用for循环来迭代:

>>> for n in fib(6):

...     print n

...

1

1

2

3

5

8

加强的生成器

在 python2.5 中,一些加强特性加入到生成器中,所以除了 next()来获得下个生成的值,用户可以将值回送给生成器[send()],在生成器中抛出异常,以及要求生成器退出[close()]

def gen(x):

    count = x

    while True:

        val = (yield count) 

        if val is not None:

            count = val

        else:

            count += 1
f = gen(5)

print f.next()

print f.next()

print f.next()

print '===================='

print f.send(9)#发送数字9给生成器

print f.next()

print f.next()

输出
5

6

7

====================

9

10

11
Python 相关文章推荐
Python生成随机密码的方法
Jun 16 Python
python中的闭包函数
Feb 09 Python
VScode编写第一个Python程序HelloWorld步骤
Apr 06 Python
Python实现获取邮箱内容并解析的方法示例
Jun 16 Python
Python定义一个跨越多行的字符串的多种方法小结
Jul 19 Python
CentOS6.9 Python环境配置(python2.7、pip、virtualenv)
May 06 Python
使用python远程操作linux过程解析
Dec 04 Python
Python字典中的值为列表或字典的构造实例
Dec 16 Python
Python打包工具PyInstaller的安装与pycharm配置支持PyInstaller详细方法
Feb 27 Python
python按顺序重命名文件并分类转移到各个文件夹中的实现代码
Jul 21 Python
基于Python-turtle库绘制路飞的草帽骷髅旗、美国队长的盾牌、高达的源码
Feb 18 Python
Pycharm制作搞怪弹窗的实现代码
Feb 19 Python
Python中函数的多种格式和使用实例及小技巧
Apr 13 #Python
在Python中使用SimpleParse模块进行解析的教程
Apr 11 #Python
Python的动态重新封装的教程
Apr 11 #Python
简单的Python的curses库使用教程
Apr 11 #Python
详解Python中的文本处理
Apr 11 #Python
状态机的概念和在Python下使用状态机的教程
Apr 11 #Python
在Python下使用Txt2Html实现网页过滤代理的教程
Apr 11 #Python
You might like
使用PHP和XSL stylesheets转换XML文档
2006/10/09 PHP
php像数组一样存取和修改字符串字符
2014/03/21 PHP
ThinkPHP2.0读取MSSQL提示Incorrect syntax near the keyword 'AS'的解决方法
2014/06/25 PHP
PHP+jquery+CSS制作头像登录窗(仿QQ登陆)
2016/10/20 PHP
Cookie跨域问题解决方案代码示例
2020/11/24 PHP
JS效率个人经验谈(8-15更新),加入range技巧
2007/01/09 Javascript
探讨javascript是不是面向对象的语言
2013/11/21 Javascript
jquery序列化表单以及回调函数的使用示例
2014/07/02 Javascript
jQuery DOM删除节点操作指南
2015/03/03 Javascript
javascript常见数据验证插件大全
2015/08/03 Javascript
jquery使用on绑定a标签无效 只能用live解决
2016/06/02 Javascript
JS基于HTML5的canvas标签实现炫目的色相球动画效果实例
2016/08/24 Javascript
js判断是否为空和typeof的用法(详解)
2016/10/07 Javascript
nodejs个人博客开发第六步 数据分页
2017/04/12 NodeJs
用纯Node.JS弹出Windows系统消息提示框实例(MessageBox)
2017/05/17 Javascript
nodejs取得当前执行路径的方法
2018/05/13 NodeJs
Vue源码解读之Component组件注册的实现
2018/08/24 Javascript
[07:20]2014DOTA2西雅图国际邀请赛 选手讲解积分赛第二天
2014/07/11 DOTA
Python基于有道实现英汉字典功能
2015/07/25 Python
Python实现KNN邻近算法
2021/01/28 Python
Python中的pathlib.Path为什么不继承str详解
2019/06/23 Python
DAWGS鞋官方网站:鞋,凉鞋,靴子
2016/10/04 全球购物
美国最大的网上冲印店:Shutterfly
2017/01/01 全球购物
美国领先的精品家居照明和装饰产品在线零售商:LightsOnline.com
2018/01/23 全球购物
台湾全方位线上课程与职能学习平台:TibaMe
2019/12/04 全球购物
留学自荐信
2013/10/10 职场文书
党员的自我评价范文
2014/01/02 职场文书
感恩寄语大全
2014/04/11 职场文书
公务员学习习总书记“三严三实”思想汇报
2014/09/19 职场文书
2015年监理工作总结范文
2015/04/07 职场文书
公务员处分决定书
2015/06/25 职场文书
春节随笔
2015/08/15 职场文书
2016党员干部反腐倡廉心得体会
2016/01/13 职场文书
Nginx 过滤静态资源文件的访问日志的实现
2021/03/31 Servers
pytorch--之halfTensor的使用详解
2021/05/24 Python
mysql中整数数据类型tinyint详解
2021/12/06 MySQL