14 - 生成器和迭代器
一、生成器
1.为什么要有生成器
-
通过列表推导式,我们可以创建一个列表,但是受到内存限制,列表的容量肯定是受限的,不仅如此如果创建一个元素很多的列表,不仅占用很大的存储空间,而且我们有时只是访问前几个元素,那么后面绝大多数元素占用的空间都白白浪费了。
所以如果列表元素可以按照某种算法推算出来,那么是否可以在循环中不断推算出后续的元素呢?这样就不必创建完整的列表,从而节省了大量空间。
2.生成器概念
- 一边循环一边计算的机制,称为生成器--generator
3.创建生成器
-
通过列表推导式得到生成器:
g = (x*3 for x in range(20))(格式和列表推导式类似,只需换成()即可)通过生成器创建列表,不会一下子开辟很多的空间,而是用一个元素就生成一个元素
-
通过函数定义生成器:
只要函数中出现了yield关键字,说明函数就不是函数了,变成生成器了
def func(): n = 0 while True: n += 1 yield n #yield的作用:返回 n 并且暂停 (return n + 暂停),等下一次调用next(),再继续接着执行 g = func() #得到生成器g步骤:
- 定义一个函数,函数中使用yield关键字
- 调用函数,接收调用的结果
- 得到的结果就是生成器
- 借助于next(),或者
__next__()得到元素
4.通过生成器生成元素的方法
-
g.__next__():每调用一次生成一个元素,指针会后移。生成器函数 -
next(g):每调用一次next则会产生一个元素。python内置函数
如果超过了生成器可以生产的元素个数,会报错
g = (x * 3 for x in range(10)) #print(next(g)) #0 #print(g.__next__()) #3 #print(next(g)) #6 while True: try: e = next(g) print(e) except: print('没有更多元素了') breakdef func(): n = 0 while True: n += 1 yield n #yield的作用:返回 n 并且暂停 (return n + 暂停),等下一次调用next(),再接着顺序执行 g = func() #得到生成器g print(g.next()) #1 print(g.next()) #2
6.生成器方法
-
__next__():获取下一个元素;如果用函数的方式定义的生成器,则获取yield返回的值 -
send(值):可以获取调用生成器中的值,也可以向每次生成器调用中传值,但注意:第一次要传的值必须是None
什么时候使用呢?如果生成器生成值的时候需要外面往里面送值,就用send
def gen(): i = 0 while i < 2: temp = yield i print(temp) for x in range(temp): print('-->',x) print('******************') i += 1 return '没有更多数据了' g = gen() print(g.send(None)) print(g.send(3)) print(g.send(5)) ''' 0 3 --> 0 --> 1 --> 2 ****************** 1 5 --> 0 --> 1 --> 2 --> 3 --> 4 ****************** Traceback (most recent call last): File "E:/PythonLearn/PycharmProjects/pythonProject/day01/test1.py", line 80, in <module> print(g.send(5)) StopIteration: 没有更多数据了 '''过程:将gen函数加载到内存中;
g = gen()先用函数的方法创建生成器(不是调用函数);然后执行g.send(None),进入到gen生成器中,顺序执行语句...,到temp = yield i,程序先执行yield i将i的值0返回并且暂停执行、temp还没有被赋值,因为暂停了。程序继续回到print(g.send(None))将返回的值0打印,接着进入下一行语句print(g.send(3)),send函数有参数3,所以会将3传入给刚才暂停的地方,而且恢复执行temp = yield i,此时将3赋值给temp,继续向下执行语句
print(temp) for i in range(temp): print('-->',i) print('******************') i += 1然后继续循环执行到
temp = yield i,又是先执行yield i,返回i值并且暂停将返回值返回给print(g.send(3)),打印1;接着执行print(g.send(5)),然后send函数执行一次,就进入到生成器中暂停的地方去,将5赋给temp,接着继续执行,这次i = 2无满足循环条件了,就将return后面的值当做报错信息返回了。注意:为什么第一个send()函数参数要为None?因为send()执行时会进入生成器中,并且进参数传进去,但是第一次进入gen(),没有什么语句能接受这个传进去的值,就会报错
5.生成器的应用
-
应用一:通过生成器生成斐波那契数列
def fib(length): #length表示斐波那契数列的长度 a, b = 0, 1 n = 0 while n < length: yield b a, b = b, a + b n += 1 return '没有更多元素了' #如果生成器无法产生元素了,那么next就接收不到元素了,此时可以通过return将信息以报错的形式发送给next(),然后打印出来 g = fib(10) while True: print(next(g)) ''' 结果为: 1 1 2 3 5 8 13 21 34 55 Traceback (most recent call last): File "E:/PythonLearn/PycharmProjects/pythonProject/day01/test1.py", line 58, in <module> print(next(g)) StopIteration: 没有更多元素了 '''
一般用return 返回报错的提示信息
-
应用二:多任务交替执行
#因为生成器中的yield有暂停的作用,可以实现多任务交替执行 def produce(n): for i in range(n): print('生产第{}'.format(i)) yield None def consume(n): for i in range(n): print('消费第{}'.format(i)) yield None g1 = produce(5) g2 = consume(5) while True: try: g1.__next__() g2.__next__() except: break ''' 生产第0 消费第0 生产第1 消费第1 生产第2 消费第2 生产第3 消费第3 生产第4 消费第4 '''
二、迭代器
1.可迭代对象
-
1.生成器(每要一个元素就可以生成一个元素)
-
2.元组、列表、字典、集合、字符串
-
如何判断对象是否为可迭代的
isinstance('abc',Iterable):将要判断的对象作为第一个参数,第二个参数为属于的类型
需要导包:from collections import Iterable
2.迭代器概念
- 每迭代一次(即在for...in...中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。那么,在这个过程中就应该有一个“人”去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们进行数据迭代的“人”称为迭代器(Iterator)。
- 一个实现了iter方法和next方法的对象,就是迭代器。
3.迭代器的作用
- 迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。
4.迭代器生成器可迭代对象关系
-
可迭代的对象是否一定是迭代器?
- 生成器是可迭代的,也是迭代器
- 列表等是可迭代的,但不是迭代器,因为调用next方法,列表没法一个一个依次得到下一个元素
-
迭代器生成器可迭代对象的关系图

生成器是迭代器的一部分,可迭代对象通过iter()可转化为迭代器
5.如何将可迭代的对象变成迭代器
-
通过内置函数
iter()将可迭代的变成一个迭代器:list --> iter(list) --> 迭代器 可调用next(迭代器)或迭代器.__next__()迭代 -
通过对象的
__iter__()函数将可迭代对象变成迭代器:list -->list.__iter__() --> 迭代器 next()或__next__()names = ['hello', 'good', 'yes'] it = names.__iter__() # 调用对象的__iter__()方法 #it = iter(names) # 调用iter()内置函数 print(it.__next__()) #hello print(it.__next__()) #good -
任意对象变成迭代器--自定义迭代器
自定义迭代器需要重写__iter__和__next__方法
class MyIterator(object): def __init__(self, n): self.n = n self.current = 0 # 自定义迭代器需要重写__iter__和__next__方法 def __iter__(self): return self def __next__(self): if self.current < self.n: value = self.current self.current += 1 return value else: raise StopIteration my_it = MyIterator(10) for i in my_it: # 迭代器重写了__iter__方法,它本身也是一个可迭代对象 print(i)
6. for...in...循环的本质、
- for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。

浙公网安备 33010602011771号