python生成器与迭代器
通过列表生成式,我们可以直接创建一个列表。但是受到内存的限制,而且创建一个包含100万个元素的列表,不仅占用的内存空间,如果我们仅仅需要访问前面几个元素,那其他元素所占用的内存空间就白白浪费了。
在python中这种一边循环一边计算的机制,称为生成器。
一、生成器
列表的元素在定义的时候就放在内容中,生成器是在使用的时候采用产生。这样做的好处就是节省空间。
#列表生成式
a = [i*2 for i in range(10)]
print(a)
通过列表创建生成器并调用
#生成器
a = (i*2 for i in range(10))
print(a)#显示的是a的内存地址
<generator object <genexpr> at 0x000001EE973CA780>
只有在被调用的时候才会生成,而且调用方法只有一个,就是通过next指令。
a = ( i*2 for i in range(10) )
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())
通过函数创建生成器,称为生生成器函数,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。(和中断的功能相似)生成器函数可以生产一个无线的序列,这样列表根本就没有办法处理。yield的作用就是把一个函数变成一个生成器,带有yield的函数不再是一个普通函数,python解释器会将其视为一个generator。
def func():
print('first')
yield 11111111
print('second')
yield 2222222
print('third')
yield 33333333
print('fourth')
g=func()
print(g)
print(g.__next__())
print(g.__next__())
一个可以无穷生产奇数的生成器函数。
def odd():
n=1
while True:
yield n
n+=2
odd_num = odd()
count = 0
for o in odd_num:
if count >=5: break
print(o)
count +=1
yield与return都有返回值的功能,但是return只能返回一次值,而yield可以返回多次值。
生成器支持的其他方法;
close( ):手动关闭生成器函数,后面的调用会直接返回StopIteration异常。
6 7 8 9 10 11 12 13 | >>> def g4(): ... yield 1 ... yield 2 ... yield 3 ... >>> g=g4() >>> next(g) 1 >>> g.close() >>> next(g) #关闭后,yield 2和yield 3语句将不再起作用 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration |
def gen():
value = 0
while True:
receive = yield value
if receive == 'e':
break
value = 'got: %s' % receive
g = gen()
print(g.send(None))
print(g.send('aaa'))
print(g.send('e'))
执行流程:
- 通过g.send(None)或者next(g)可以启动生成器函数,并执行到第一个yield语句结束的位置。此时,执行完了yield语句,但是没有给receive赋值。yield value会输出初始值0注意:在启动生成器函数时只能send(None),如果试图输入其它的值都会得到错误提示信息。
- 通过g.send(‘aaa’),会传入aaa,并赋值给receive,然后计算出value的值,并回到while头部,执行yield value语句有停止。此时yield value会输出”got: aaa”,然后挂起。
- 通过g.send(3),会重复第2步,最后输出结果为”got: 3″
- 当我们g.send(‘e’)时,程序会执行break然后推出循环,最后整个函数执行完毕,所以会得到StopIteration异常。
二、迭代器
可直接作用于for循环的数据类型:一类是集合数据类型,如list、tuple、dict、set、str等;一类是生成器。可以直接作用于for循环的对象称为可迭代对象,Iterable。可以用isinstance()判断一个对象是否可迭代对象,如下所示:
from collections import Iterable
print(isinstance([],Iterable))
print(isinstance({ },Iterable))
print(isinstance(( ),Iterable))
print(isinstance('abc',Iterable))
print(isinstance(10,Iterable))
print(isinstance((x for x in range(10)),Iterable))
可以被next( )函数调用并不断返回下一值的对象称为迭代器:Iterator。也可以使用isinstance来判断一个对象是否是迭代对象。
from collections import Iterator
print(isinstance([],Iterator))
print(isinstance({ },Iterator))
print(isinstance(( ),Iterator))
print(isinstance('abc',Iterator))
print(isinstance(10,Iterator))
print(isinstance((x for x in range(10)),Iterator))
生成器都是Iterator对象,但List、dict、str虽然都是Iterable,但不是Iterator。把list、dict、str等可迭代对象变成迭代器可以使用iter( )函数:print(isinstance(iter([ ]),Iterator))
三、小结
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
python的for循环本质上就是通过不断调用next()函数实现的。
python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,知道没有数据是抛出StopIteration错误。如果遇到了return,则直接抛出stopiteration终止迭代。
4 5 6 7 8 9 10 11 12 | >>> def g2(): ... yield 'a' ... return ... yield 'b' ... >>> g=g2() >>> next(g) #程序停留在执行完yield 'a'语句后的位置。 'a' >>> next(g) #程序发现下一条语句是return,所以抛出StopIteration异常,这样yield 'b'语句永远也不会执行。 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration |
- 按照鸭子模型理论,生成器就是一种迭代器,可以使用for进行迭代。
- 第一次执行next(generator)时,会执行完yield语句后程序进行挂起,所有的参数和状态会进行保存。再一次执行next(generator)时,会从挂起的状态开始往后执行。在遇到程序的结尾或者遇到StopIteration时,循环结束。
- 可以通过generator.send(arg)来传入参数,这是协程模型。
- 可以通过generator.throw(exception)来传入一个异常。throw语句会消耗掉一个yield。可以通过generator.close()来手动关闭生成器。
- next()等价于send(None)