Python中的生成器与yield
对于python中的yield有些疑惑,然后在StackOverflow上看到了一篇回答,所以搬运过来了,英文好的直接看原文吧。
可迭代对象
当你创建一个列表的时候,你可以一个接一个地读取其中的项。一个接一个地读项就叫做迭代:
>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3
mylist
就是一个可迭代对象。你使用列表推导式时,就创建了一个列表,也就是一个可迭代对象:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
任何可以用“for...in...
”操作的事物都是可迭代对象;列表,字符串,文件……
这些可迭代对象很方便,因为你可以随意地访问它们,但是你把所有的值都保存在内存中。如果你有大量的数据的话,这就不是你想要的了。
生成器
生成器是迭代器,但是你只可以对它们进行一次迭代。这是因为你没有在内存中存储所有数值,它们动态地生成值:
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
跟上面的例子基本一样,除了用()
代替了[]
。但是,你不能第二次执行for i in mygenerator
,因为生成器只能使用一次;它计算出0,然后忘掉0并计算1,最后是计算4,一个接一个地进行。
Yield
Yield
是一个像return
一样的关键字,只不过函数返回的是一个生成器:
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # 创建一个迭代器
>>> print(mygenerator) # mygenerator是一个对象!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
>>> for i in mygenerator:
print(i)
>>>
这个例子里的生成器没什么用,但是如果你知道你的函数会返回大量的数据,而你只需要对这些数据读取一次时,那就变得很有用了。
要掌握yield
,你必须理解当你访问函数的时候,你写在函数体中的代码并没有执行。这个函数只是返回了迭代器对象,这一点很微妙。
然后,每次for
语句使用生成器的时候,你的代码就会运行。
然后是最难的部分:
for
语句第一次调用从你的函数中创建的迭代器对象时,它就会运行你函数中的代码,从开始一直到它碰见yield
,然后它就返回这个循环中的第一个值。之后,每一次的调用都会再一次运行你写在函数里的循环,然后返回下一个值,直到没有值可以被返回。
一旦函数运行但是没有再碰到yield
,生成器就被认为是空的。这可能是因为循环已经结束了,或者因为不再满足if/else
条件了。
控制生成器耗尽
>>> class Bank(): # let's create a bank, building ATMs
... crisis = False
... def create_atm(self):
... while not self.crisis:
... yield "$100"
>>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # it's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # build a new one to get back in business
>>> for cash in brand_new_atm:
... print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...
这个在很多方面都有用,比如控制对某个资源的访问。
Itertools,好帮手
itertools模块包含操作可迭代对象的特殊函数。想要复制一个生成器?连接两个生成器?把嵌套列表中的数据整理到一个列表中?不创建另一个列表就直接Map/Zip
?
那就import itertools
。
例子?那我们看看4马比赛中所有可能的到达顺序(全排列):
>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
(1, 2, 4, 3),
(1, 3, 2, 4),
(1, 3, 4, 2),
(1, 4, 2, 3),
(1, 4, 3, 2),
(2, 1, 3, 4),
(2, 1, 4, 3),
(2, 3, 1, 4),
(2, 3, 4, 1),
(2, 4, 1, 3),
(2, 4, 3, 1),
(3, 1, 2, 4),
(3, 1, 4, 2),
(3, 2, 1, 4),
(3, 2, 4, 1),
(3, 4, 1, 2),
(3, 4, 2, 1),
(4, 1, 2, 3),
(4, 1, 3, 2),
(4, 2, 1, 3),
(4, 2, 3, 1),
(4, 3, 1, 2),
(4, 3, 2, 1)]
理解迭代的内部机制
迭代是一个暗含可迭代对象(实现了__iter__()
方法)和迭代器(实现了__next__()
方法)的过程。可迭代对象是任何你可以从中得到迭代器的对象。迭代器是可以让你对可迭代对象进行迭代的对象。
更多信息请参考how does the for loop work。
Python中的生成器与yield的更多相关文章
- python中的生成器函数是如何工作的?
以下内容基于python3.4 1. python中的普通函数是怎么运行的? 当一个python函数在执行时,它会在相应的python栈帧上运行,栈帧表示程序运行时函数调用栈中的某一帧.想要获得某个函 ...
- Python学习-39.Python中的生成器
先回顾列表解释 lista = range(10) listb = [elem * elem for elem in lista] 那么listb就将会是0至9的二次方. 现在有这么一个需求,需要存储 ...
- python中的生成器(二)
一. 剖析一下生成器对象 先看一个简单的例子,我们创建一个生成器函数,然后生成一个生成器对象 def gen(): print('start ..') for i in range(3): yield ...
- 深入理解Python中的生成器
生成器(generator)概念 生成器不会把结果保存在一个系列中,而是保存生成器的状态,在每次进行迭代时返回一个值,直到遇到StopIteration异常结束. 生成器语法 生成器表达式: 通列表解 ...
- python迭代器与生成器及yield
一.迭代器(itertor) 1.可迭代: 在Python中如果一个对象有__iter__()方法或__getitem__()方法,则称这个对象是可迭代的(iterable). 其中__iter__( ...
- 聊聊Python中的生成器和迭代器
Python中有两个重要的概念,生成器和迭代器,这里详细记录一下. 1. 生成器 什么是生成器呢? 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包 ...
- python中的生成器(一)
我们先考虑一个场景: 有个情景需要循环输出1——10. 这里给两种方法: list1 = [1,2,3,4,5,6,7,8,9,10] for i in list1: print(i) for i i ...
- python中的生成器(generator)总结
1.实现generator的两种方式 python中的generator保存的是算法,真正需要计算出值的时候才会去往下计算出值.它是一种惰性计算(lazy evaluation). 要创建一个gene ...
- python学习之【第十三篇】:Python中的生成器
1.为什么要有生成器? 在Python中,通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅 ...
随机推荐
- Beta版总结会议
一,设想和目标 1. 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的app要解决一个令人头痛但无人具体来实现的这一社会困扰现象.因为学生或者社会人士在参加竞 ...
- Ubuntu14.04 lamp环境 php mongodb扩展安装
安装pecl支持: sudo apt-get install php5-dev php5-cli php-pear 安装mongo驱动 sudo pecl install mongo 修改php.in ...
- MySQL报错:Got error 28 from storage engine
今天碰到数据库出错: Got error 28 from storage engine 查了一下,数据库文件所在的盘应该没事,应该是数据库用的临时目录空间不够 问题原因: 磁盘临时空间不够导致. 解决 ...
- 转载:Centos7 从零编译Nginx+PHP+MySql 二
序言 这次玩次狠得.除了编译器使用yum安装,其他全部手动编译.哼~ 看似就Nginx.PHP.MySql三个东东,但是它们太尼玛依赖别人了. 没办法,想用它们就得老老实实给它们提供想要的东西. 首先 ...
- em与px换算关系以及常用列表
1.任意浏览器的默认字体大小都是16px.2.所有未经调整的浏览器都符合: 1em=16px 12px=0.75em 10px=0.625em3.为了简化font-size的换算,在body选择器中声 ...
- Hadoop学习记录
http://blog.csdn.net/m_star_jy_sy/article/details/26476907配置windows里eclipse连接hadoop集群 hadoop常见命令 启动H ...
- Oracle的动态性能视图[持续更新]
前言 .... v$version:查看数据库版本 其中 Oracle Database:指代数据库版本 PL/SQL:ORACLE对于标准SQL的超集,全称Procedural Language/S ...
- Can I Win
In the "100 game," two players take turns adding, to a running total, any integer from 1.. ...
- redirect问题
场景如下: 在后台写了一个filter,拦截*.wx的请求,filter内逻辑忽略,最后response.sendRedirect(url)(这个url是相对地址),重定向到另一个页面. 问题来了:一 ...
- FusionCharts和highcharts 饼图区别!
FusionCharts ---------------脚本--------------- <script src="../../../fashioncharts/js/FusionC ...