生成器
在讲生成器之前,有必要先提一下列表生成式,列表生成式之前已经提过,它也叫列表解析式,是一种生成列表的高级用法,再来回顾一下:
给出列表 list = [1,2,3,4,5] ,生成它的每一个元素都平方后的列表:
怎么实现呢?如果用循环,那就又要折腾好几行代码了,而如果用列表解析式,一行搞定:
listx = [v**2 for v in list]
print(listx) #[1, 4, 9, 16, 25]
如上,通过列表生成式,我们可以创建一个列表,但是受到内存限制,列表容量肯定是有限的,或者我们创建了一个很大的列表,却只需要用到前面几个数据,那么很多空间就都被浪费了;
于是就有人琢磨,如果列表中的元素可以被某种算法推导出来,那么不就可以边推导边生成,只需要利用一个循环在得到我们想要的数据后就停止生成,那么不就可以节省大量空间了?
所以就有了生成器;
在Python中,这种一边循环一边计算的机制,称为生成器(generator);
创建一个生成器
方法一:
第一种创建生成器的方法很简单,只需要把列表生成式的[]、或者{}改为()即可,这对列表或者字典都适用;
list = [1,2,3,4,5]
gtor = (v**2 for v in list)
print(type(gtor)) #<class 'generator'>
列表和用列表创建的生成器有一点不同就在于生成器已经不能用print直接输出全部了,因为它只是一个生成器,所以当你用print输出生成器,会输出:
list = [1,2,3,4,5]
gtor = (v**2 for v in list)
print(gtor) #<generator object <genexpr> at 0x03D85870>
如果你要遍历生成器内的元素,可以用next()函数;
next()函数的作用是返回generator的下一个元素,如果没有下一个元素,抛出 StopIteration 的错误;
注意next()函数的指向从第一个元素开始,不会后退,只会向前;
但这是不推荐的,事实上直接用for循环遍历就好,next()会显得有些臃肿;
方法二:
如果一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator,调用函数就是生成一个generator对象;
同样上例子:
先编写一个函数求斐波那契数列:
def fib(max):
n,a,b = 0,0,1
while n<max:
print(b,end="")
print(' ',end="")
a,b = b,a+b
n += 1
fib(5) #1 1 2 3 5
按照方法二所述,怎么把这个函数变成一个生成器呢?这里只要把print(b)的语句改为yield b就可以了;
def fib(max):
n,a,b = 0,0,1
while n<max:
yield b
print(' ',end="")
a,b = b,a+b
n += 1
return 'done' #这里加一个返回值
这样函数fib()就成了一个生成器,之后next()遍历还是for循环都随便你;
f = fib(6)
for v in range(1,7):
print(next(f),end="") #1 1 2 3 5 8
注意这条 f = fib(6) 的语句,fib(6)生成了一个有六个元素的生成器并使f指向它,因此range最多到7,超过7就会报错;
或者循环更方便:
for v in fib(6):
print(v,end="") #1 1 2 3 5 8
这里要理解yield的机制:普通函数是顺序执行,遇到return或者执行到做后一条语句就返回,然而变成generator的函数,在遇到yield语句时就会返回,在本例中是yield b ,所以每次都返回b,当再次执行时再从上次中断的地方继续执行;
另外还有一个问题,怎么输出函数的返回值呢?即本例中的 ‘done’,可以参照下面例子:
f = fib(7)
while True:
try:
print(next(f),end="")
except StopIteration as s:
print(s.value,end="") #s.value是捕获到StopIteration错误时得到的返回值
break
关于yield的更详细说明有一篇技术大牛写的文章个人感觉贼拉好,一并附上:
https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/
迭代器
Python中,迭代器是一个很强大的功能,它是一个可以记住遍历位置的对象,迭代器只作用于可迭代对象,那么什么是可迭代对象呢?
可迭代对象在Python中叫做Iterable,它是一种数据类型,我们可以通过isinstance()函数来判断一个对象是否是可迭代对象:
>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance('',Iterable)
True
>>> isinstance(666,Iterable)
False
可以看出,列表、字典、字符串等都是可迭代对象,而数值型不是;
要注意的是generator也是一类特殊的可迭代对象;
可迭代对象都有一个特点,就是可以直接作用于for循环,也就是说它们都可以使用类似列表解析式的构建方法;
而可以被next()函数调用并不断返回下一个值得对象成为迭代器(Iterator),迭代器有两个基本的方法:iter()和next():
<注意>这里要格外区分清楚Iterator与Iterable,一个是迭代器,一个是可迭代对象;其中所有generator对象都是Iterator对象,而像列表list、字典dict等虽然是Iterable,但不是Iterator;
把list、dict、str等Iterable变成Iterator需要用到iter()函数:
>>> isinstance(iter([]), Iterator)
True
部分内容(不限于本文)参考自: