python之生成器和迭代器

1、生成器

生成器:如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
回顾列表推导式

my_list = [x for x in range(10)]
print(my_list) # 列表推导式:直接包含所有数据的列表

(1)第一种方法产生一个生成器:

my_generator = (x for x in range(10))
print(my_generator) # 生成器:一个生成器对象,包含算法

我们可以打印出列表,但是怎么输出generator的每个值呢?

# 通过系统的内建函数next()获取生成器中下一个数据
print(next(my_generator)) # 0
print(next(my_generator)) # 1
print(next(my_generator)) # 2
print(next(my_generator)) # 3
# (2)通过类型的 __next__()魔法方法,直接获取下一个数据
print(my_generator.__next__()) # 4
print(my_generator.__next__()) # 5
print(my_generator.__next__()) # 6
print(my_generator.__next__()) # 7
print(my_generator.__next__()) # 8
print(my_generator.__next__()) # 9
print(my_generator.__next__()) # StopIteration

我们讲过generator里保存的是算法,每次调用next(),就计算出my_generator的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
但是每次取值都使用next(),有点太麻烦,而且还要监听StopIteration异常,所以我们可以使用,for循环遍历。并且,当生成器没有元素是for循环自动停止。

for i in my_generator:
    print(i,end=" ")
# 结果:0 1 2 3 4 5 6 7 8 9 

(2)第二种方法产生一个生成器:

著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1,1,2,3,5,8,13,21……
斐波拉契数列用列表推导式是写不出来的,因为它是无穷无尽的,但是我们可以看的出来,它是有规则的,可以通过算法表达出来!

def fbi(max):
    n,a,b = 0,0,1
    while n < max:
        print(b,end=" ") #end=" "让print()不再默认换行输出,而是空格输出
        a,b = b,a + b
        n += 1
    return 'done'

fbi(10)
# 结果:1 1 2 3 5 8 13 21 34 55 

此时的斐波拉列函数离生成器只有一步之遥!只要将print(b,end=” “)换成yield,就变成generator的函数

def fbi(max):
    n,a,b = 0,0,1
    while n < max:
        yield b
        a,b = b,a + b
        n += 1
    return 'done'

fb = fbi(10)
print(type(fb))
# 结果:<class 'generator'>

一般取generator的用for循环遍历,但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

while True:
    try:
        x = next(b)
        print("b:",x)
    except StopIteration as e:
        print('Generator return value:', e.value)
        break

# 结果:
b: 1
b: 1
b: 2
b: 3
b: 5
b: 8
b: 13
b: 21
b: 34
b: 55
Generator return value: done

2、迭代器

我们知道直接可以使用for循环遍历的数据有:
一类:list、tuple、set、dict和str等
还有一类:generator,包括生成器和带yield的生成器函数
这些可以直接使用for循环遍历的对象,统称为:可迭代对象(Iterabel)
可以使用类型判断函数isinstance()判断

import collections
my_set = {1,2,3,4,5}
my_tuple = (1,2,3,4,5)
my_dict = {1:"2",2:"3"}
my_list = (1,2,3,4)
my_generator = (x for x in range(10))

print(isinstance(my_tuple,collections.Iterable)) 
print(isinstance(my_set,collections.Iterable))  
print(isinstance(my_dict,collections.Iterable)) 
print(isinstance(my_list,collections.Iterable)) 
print(isinstance(my_generator,collections.Iterable))

# 结果:
True
True
True
True
True

生成器不但可以被for遍历,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
同样也可以用isinstance()判断

print(isinstance(my_tuple,collections.Iterator))
print(isinstance(my_set,collections.Iterator))
print(isinstance(my_dict,collections.Iterator))
print(isinstance(my_list,collections.Iterator))
print(isinstance(my_generator,collections.Iterator))

# 结果:
False
False
False
False
True

生成器都是Iterator对象,但list、dict、str、set 、tuple虽然是Iterable,却不是Iterator。
把list、dict、str、tuple、set 等Iterable变成Iterator可以使用iter()函数:

print(isinstance(iter(my_tuple),collections.Iterator))
print(isinstance(iter(my_set),collections.Iterator))
print(isinstance(iter(my_dict),collections.Iterator))
print(isinstance(iter(my_list),collections.Iterator))

# 结果:
True
True
True
True

为什么list、set、tuple、dict等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。


3、小结

1、凡是可用于for循环的对象都是:Iterabel类型
2、凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
3、生成器与迭代器的区别:
生成器:用来产生数据的
迭代器:用来遍历数据的

本文参照了廖雪峰老师的文章,但也加入了自己的一些理解!

猜你喜欢

转载自blog.csdn.net/qq_41963640/article/details/82082973