上节内容说到Python的for语句循环本质上就是通过调用Iterable可迭代对象的__iter()__方法获得一个Iterator迭代器对象,然后不断调用Iterator迭代器对象__next()__方法实现的。Iterator迭代器对象则是一个需要实现__iter__()__next__()两个迭代器协议方法的对象。python中生成器提供了一种方便的方法来实现迭代器协议,而不需要必须实现__iter__()__next__()两个迭代器协议方法。

生成器的定义方式有两种,一种是调用生成器函数,一种是使用生成器表达式语法。

调用生成器函数

生成器函数是指在函数体中使用yield表达式仅返回结果的函数。yield表达式仅在定义生成器函数时使用,因此只能用在函数定义的主体中。在函数体中使用yield表达式会使该函数成为生成器函数。当生成器函数被调用时,它返回一个称为生成器的迭代器,该迭代器由python自动生成。然后,生成器控制了生成器函数的执行。因为返回的生成器是一个迭代器,所以生成器函数的执行结果也就可以被循环。当生成器的的__next__方法被调用时,生成器函数的函数体内的语句开始执行,执行进行到第一个yield表达式时,立即将yield表达式的结果返回给生成器的调用者,同时将生成器函数内部的状态挂起。即保持生成器函数的执行进度,和生成器函数内的局部状态:包括局部变量的当前绑定,指令指针,内部计算栈和任何异常处理的状态。当生成器的再次调用__next__方法来时,生成器函数恢复执行,并再次执行到yield表达式返回结果再保持状态,直到无法再执行到yield表达式。此时生成器自动抛出StopIteration异常。

我们先定义一个简单生成器函数,函数功能返回数字0-9的平方数

# 生成器函数,功能返回数字0-9的平方数
>>> def squares():
for i in range(10):
yield i**2 # 使用return关键字是普通函数,使用yield关键字函数变成了生成器函数

使用参数g接收调用生成器函数squares的结果,然后分别在shell查看squaresg这两个变量的类型

>>> g = squares()

#查看squares对象类型
>>> squares
<function squares at 0x035950C0> #查看g对象类型
>>> g
<generator object squares at 0x0358A930>

从上面可以看出变量squares是函数类型,变量ggenerator类型对象,generator从字面的理解上就是生成器类型。根据上一节迭代器中提到的知识,从collection模块引入Iterator的抽象基类,验证下generator类型是不是上面说的迭代器类型。

>>> from collections import Iterator
>>> isinstance(g,Iterator)
True

验证成功,说明生成器函数的执行结果确实是生成器,一种特殊的迭代器。

>>> for i in g:
print (i) 0
1
4
9
16
25
36
49
64
81

生成器表达式

除了使用生成器函数可以得到生成器,还可以生成器表达式得到生成器表达式。生成器表达式本身看起来像列表推到, 但不是用方括号而是用圆括号包围起来:

>>> g2 = (x**2 for x in range(10))
>>> g2
<generator object <genexpr> at 0x0359AFC0> >>> t = (1,2,3,4,5)
>>> g3 = (x**2 for x in t)
>>> g3
<generator object <genexpr> at 0x007F6180>

验证:

>>> isinstance(g2,Iterator)
True >>> isinstance(g3,Iterator)
True

使用:

>>> for i in g2:
print(i) 0
1
4
9
16
25
36
49
64
81 >>> for i in g3:
print(i) 1
4
9
16
25

和普通迭代器相比,生成器不单简化了迭代器的定义,还在使用效率上有提升。因为生成器在循环时,生成器函数每次只会返回一个结果,然后保持内部状态,所以生成器占用的内存是很小的。以下两个测试结果,第一个直接抛出MemoryError异常,第二个只能正确计算出结果。

# 全部数据先加载在1个列表上面,内存占用高
>>> s1 = sum([i for i in range(100000000)])
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
s1 = sum([i for i in range(100000000)])
File "<pyshell#6>", line 1, in <listcomp>
s1 = sum([i for i in range(100000000)])
MemoryError # 数据几乎不占内存
>>> s2 = sum((i for i in range(100000000)))
>>> s2
4999999950000000

最新文章

  1. paip.执行shell cmd 命令uapi java php python总结
  2. 用java发送邮件(黄海已测试通过)
  3. duilib中的V和H布局中滚动条问题
  4. mapreduce 倒排索引的建立
  5. JAVA去掉字符串前面的0
  6. Date、DateTime值的格式化扩展函数
  7. jquery拖拽插件 tableDnD
  8. #使用parser获取图片信息,输出Python官网发布的会议时间、名称和地点。
  9. OWLQN算法
  10. Cannot load browser &quot;PhantomJS&quot;: it is not registered! Perhaps you are missing some plugin? 测试安装遇到的BUG
  11. iKcamp团队制作|基于Koa2搭建Node.js实战(含视频)☞ 路由koa-router
  12. centos7下kafka集群安装部署
  13. iOS - 集成高德SDK解决Marker点重复点击无效问题
  14. Codeforces Round #467 (Div. 2) B. Vile Grasshoppers
  15. 从零开始学 Web 之 JavaScript(一)JavaScript概述
  16. 用Delphi制作动态菜单 该文章《用Delphi制作动态菜单》
  17. 23-Python3 File
  18. bs4.BeautifulSoup的基础用法
  19. 第七章&#160;二叉搜索树(b3)BST:删除
  20. html lang=&quot;zh-cn&quot;解决Mac版Firefox中文字体显示问题

热门文章

  1. python 路径处理
  2. Cobbler安装CentOS7系统时报错 curl:(7)Failed connect to 10.0.0.201:80;Connection refused
  3. zabbix3.4 监控ESXI6.7
  4. Git常用命令及场景
  5. ie每次登陆出现:Windows安全性 iexplore.exe 正在连接到 记住我的凭证不起作用
  6. 代码之髓读后感——容器&amp;并发
  7. c++ stl源码剖析学习笔记(二)iterator
  8. 状态机学习(六)解析JSON2
  9. SSM框架整合(Spring+SpringMVC+MyBatis+Oracle)
  10. 【repost】jQuery笔记总结