生成器与迭代器
迭代器与生成器
主要内容:
- 迭代器的介绍
- 迭代器的取值
- for循环的原理
- 生成器
- 生成器传值
1. 迭代器介绍
迭代的概念是循环的执行某一过程,且后一次执行会用到前一次的结果,在Python中的迭代,就一般是指利用for循环迭代取值。Python中支持迭代的基本数据类型主要有字符串,列表,元组等,这些都可以算是可迭代对象,那么什么是可迭代对象,什么又是迭代器呢?
可迭代对象可以理解为内置有__iter__方法的对象,它返回的是一个迭代器,迭代器就是一个实现了__iter__方法和__next__的方法的对象,简而意之,可迭代对象未必是迭代器,迭代器一定是可迭代对象。例如我们通用的列表等数据类型就只是一个可迭代对象,而利用open方法产生的文件对象就是一个迭代器。
2. 迭代器的取值
我们在利用for循环取值的时候,其实解释器内部调用的其实是可迭代对象的__iter__方法,如下所示,遍历列表的过程
l = [1, 2, 3, 4]
for i in l.__iter__():
print(i)
print(l.__iter__())
1 2 3 4 <list_iterator object at 0x0000020EA8017CC0>
list对象被for循环时生成了一个list_iterator,然后依次迭代输出,那迭代器是如何取值的呢?迭代器是通过每次调用它的__next__方法,直到迭代器的值被取完后,抛出StopIteration异常为止。
迭代器有几个特殊的特点,迭代器本身无论调用多少次__iter__方法,得到的都是它自己,此外迭代器迭代的过程只能一路向前,不能回退取值。
3. for循环的原理
了解了__iter__方法和__next__的方法,其实就已经了解了for循环的本质了,for循环其实就是内部帮我们先获得可迭代对象的__iter__方法,获得迭代器, 然后不断调用迭代器的__next__方法,值取完后,最后通过捕获StopIteration异常,防止程序异常。实现的方法如下:
l = [1, 2, 3, 4]
l_iter = l.__iter__()
while True:
try:
print(l_iter.__next__(), end=' ')
except StopIteration:
break
1 2 3 4
4. 生成器
生成器就是一种一次只能产生一个值的特殊函数,它本质上就是一个用户自定义的迭代器。使用好生成器可以在处理一些大文件的时候大大节省内存,因为取一个数才生成一个。在Python中定义生成器主要有两种方式第一种是函数体中存在yield关键字,第二种是利用生成器表达式。
def gen():
for i in range(1, 4):
yield i
gen2 = (i for i in range(2, 5))
print(gen, gen2)
<function gen at 0x0000029AA9532E18> <generator object <genexpr> at 0x0000029AB890F990>
由上面可知生成器比较特殊的地方在于它在利用函数定义完之后,它还是一个函数,只有像普通函数一样调用后,它才会由函数转变为生成器。之后我们可以把它当做一个迭代器,每一次利用__next__取值时,生成器就会运行到下一个yield表达式处,每一次产出的值就是yield后面的值,此处可以同时产出多个值,会以元组的形式返回。生成器像函数一样运行完毕后,会像普通迭代器一样,抛出StopIteration异常,所以说生成器本质上就是一个迭代器。
5. 生成器传值
生成器另一个特殊的地方在于可以向它传值,可以利用send函数向生成器的第一处yield处传值,为什么这么说呢?因为send传的值,生成器是通过yield左边的变量接收的,这只有当生成器启动之后,且运行到第一处后,才生效。不然生成器刚定义完毕,就调用send方法传入非None值就会报错。
def gen():
print('1')
recv = yield 5
print('2')
print(recv)
yield
g = gen()
next(g)
g.send(4)
1 2 4
从这个例子看出来生成器还可以通过send方法取值,send方法也会促使生成器向前运行。
6. 生成器面试题
def add(n, i):
return n + i
def test():
for i in range(4):
yield i
g = test()
for n in [1, 10]:
g = (add(n, i) for i in g)
print(n)
res = list(g)
分析一下这个面试题,需要牢记生成器的核心,生成器定义后只是在一处内存空间存储了代码,并没有真正执行。此处其实产生了3个生成器,
就是最后的g = (add(n, i) for i in (add (n, i) for i in test()))
最后n是全局变量,它的值是10, 所以最右边的 test()产生的值是0, 1, 2, 3, 所以第一个in处和add函数作用后产生的值是10,11,12,13,最后再依次和10相加,所以这个答案是20, 21, 22, 23

浙公网安备 33010602011771号