迭代器&生成器
一、可迭代的&迭代器
可迭代协议
①协议内容:内部实现了__iter__方法
②验证方法:dir()方法;使用dir()方法查看数据类型中的方法是否包含__iter__
③__iter__()方法的作用:可迭代的数据类型执行__iter__()方法后会生成一个迭代器对象
④例子:
1 print([1,2].__iter__()) 2 # 结果 3 <list_iterator object at 0x1024784a8>
1 """ 2 dir([1,2].__iter__())是列表迭代器中实现的所有方法,dir([1,2])是列表中实现的所有方法, 3 都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合,然后取差集。 4 """ 5 #print(dir([1,2].__iter__())) 6 #print(dir([1,2])) 7 print(set(dir([1,2].__iter__()))-set(dir([1,2]))) 8 9 结果: 10 {'__length_hint__', '__next__', '__setstate__'} 11 12 # 迭代器中多出的三个方法介绍 13 iter_l = [1,2,3,4,5,6].__iter__() 14 15 #获取迭代器中元素的长度 16 print(iter_l.__length_hint__()) 17 18 #根据索引值指定从哪里开始迭代 19 print('*',iter_l.__setstate__(4)) 20 #一个一个的取值 21 print('**',iter_l.__next__()) 22 print('***',iter_l.__next__())
二、生成器
①种类:python中提供的生成器有:
- 生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
- 生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
②本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
③特点:惰性运算,开发者自定义
④生成器函数:一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
- 1) 生成器函数例子:
1 import time 2 def genrator_fun1(): 3 a = 1 4 print('现在定义了a变量') 5 yield a 6 b = 2 7 print('现在又定义了b变量') 8 yield b 9 10 g1 = genrator_fun1() 11 print('g1 : ',g1) #打印g1可以发现g1就是一个生成器 12 print('-'*20) #我是华丽的分割线 13 print(next(g1)) 14 time.sleep(1) #sleep一秒看清执行过程 15 print(next(g1))
2) 生成器函数的好处:可以避免一次性读取数据到内存中导致内存溢出
1 # 例子01: 2 def produce(): 3 """生产衣服""" 4 for i in range(2000000): 5 yield "生产了第%s件衣服"%i 6 7 product_g = produce() 8 print(product_g.__next__()) #要一件衣服 9 print(product_g.__next__()) #再要一件衣服 10 print(product_g.__next__()) #再要一件衣服 11 num = 0 12 for i in product_g: #要一批衣服,比如5件 13 print(i) 14 num +=1 15 if num == 5: 16 break 17 18 #到这里我们找工厂拿了8件衣服,我一共让我的生产函数(也就是produce生成器函数)生产2000000件衣服。 19 #剩下的还有很多衣服,我们可以一直拿,也可以放着等想拿的时候再拿 20 21 # 例子02: 22 import time 23 def tail(filename): 24 f = open(filename) 25 f.seek(0, 2) #从文件末尾算起 26 while True: 27 line = f.readline() # 读取文件中新的文本行 28 if not line: 29 time.sleep(0.1) 30 continue 31 yield line 32 33 tail_g = tail('tmp') 34 for line in tail_g: 35 print(line) 36 # 例子03: 37 def averager(): 38 total = 0.0 39 count = 0 40 average = None 41 while True: 42 term = yield average 43 total += term 44 count += 1 45 average = total/count 46 47 g_avg = averager() 48 next(g_avg) 49 print(g_avg.send(10)) 50 print(g_avg.send(30)) 51 print(g_avg.send(5)) 52 # 例子04: 53 def init(func): #在调用被装饰生成器函数的时候首先用next激活生成器 54 def inner(*args,**kwargs): 55 g = func(*args,**kwargs) 56 next(g) 57 return g 58 return inner 59 60 @init 61 def averager(): 62 total = 0.0 63 count = 0 64 average = None 65 while True: 66 term = yield average 67 total += term 68 count += 1 69 average = total/count 70 71 g_avg = averager() 72 # next(g_avg) 在装饰器中执行了next方法 73 print(g_avg.send(10)) 74 print(g_avg.send(30)) 75 print(g_avg.send(5))
3) yield from
1 def gen1(): 2 for c in 'AB': 3 yield c 4 for i in range(3): 5 yield i 6 print(list(gen1())) 7 8 以上是将生成器中的数据以列表的方式打印 9 以下的执行效果与上面相同,运用了yield from 语法 10 def gen2(): 11 yield from 'AB' 12 yield from range(3) 13 14 print(list(gen2()))
⑤生成器函数进阶
send():
- send和next的作用相同
- 第一次不能用send
- 函数中的最后一个yield不能接受新的值
1 # 计算移动平均值的例子 2 def average(): 3 sum = 0 4 count = 0 5 avg = 0 6 while True: 7 num = yield avg 8 sum += num 9 count += 1 10 avg = sum / count 11 12 avg_g = average() 13 avg_g.__next__() 14 15 print(avg_g.send(20)) 16 print(avg_g.send(40)) 17 print(avg_g.send(60))
1 # 计算移动平均值的例子 2 def average(): 3 sum = 0 4 count = 0 5 avg = 0 6 while True: 7 num = yield avg 8 sum += num 9 count += 1 10 avg = sum / count 11 12 avg_g = average() 13 avg_g.__next__() 14 15 print(avg_g.send(20)) 16 print(avg_g.send(40)) 17 print(avg_g.send(60))
⑥生成器表达式
1 ''' 2 列表推导(列表生成式) 3 ''' 4 5 # 简单的列表推导 6 l = [i ** 2 for i in range(10)] 7 print(l) 8 9 ''' 10 生成器表达式 11 与列表推导相似,返回生成器对象 12 ''' 13 14 g = (i ** 2 for i in range(10)) 15 16 # 生成器的第一种调用方式:__next__ 17 print(g.__next__()) 18 19 # 生成器的第二种调用方式:for循环 20 for i in g: 21 print(i) 22 23 # 生成器的第三种调用方式:类型强转 24 print(list(g))
⑦各种推导式
1 ''' 2 列表推导 3 [每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型] #遍历之后挨个处理 4 [满足条件的元素相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件] #筛选功能 5 ''' 6 # 简单的列表推导 7 l = [i ** 3 for i in range(10)] 8 print(l) 9 >>> [0, 1, 8, 27, 64, 125, 216, 343, 512, 729] 10 11 # 带筛选的列表推导 12 l = [i ** 3 for i in range(10) if i % 3 == 0] 13 print(l) 14 >>> [0, 27, 216, 729] 15 16 # 多层列表推导 17 double_l = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], 18 ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] 19 l = [name for lst in double_l for name in lst if name.count('e') >= 2] 20 print(l) 21 >>> ['Jefferson', 'Wesley', 'Steven', 'Jennifer'] 22 23 ''' 24 字典推导 25 ''' 26 # 例一:将一个字典的key和value对调 27 mcase = {'a': 10, 'b': 34} 28 mcase_frequency = {mcase[k]: k for k in mcase} 29 print(mcase_frequency) 30 >>> {34: 'b', 10: 'a'} 31 32 # 例二:合并大小写对应的value值,将k统一成小写 33 mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3} 34 mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()} 35 print(mcase_frequency) 36 >>> {'b': 34, 'a': 17, 'z': 3} 37 38 ''' 39 集合推导 40 ''' 41 # 计算列表中每个值的平方,自带去重功能 42 squared = {x ** 2 for x in [1, -1, 2]} 43 print(squared) 44 >>> {1, 4} 45 46 ''' 47 练习题 48 ''' 49 50 # 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母 51 lst = ['123', 'ab', 'y', 'abcabc', 'okiuj'] 52 l = [i.upper() for i in lst if len(i) > 3] 53 print(l) 54 >>> ['ABCABC', 'OKIUJ'] 55 56 # 答案 57 # [name.upper() for name in names if len(name)>3] 58 59 # 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元组列表 60 lst = [(4, 3), (3, 2), (8, 1), (8, 6)] 61 t = [i for i in lst if ((i[0] % 2 == 0) and (i[1] % 2 != 0))] 62 print(t) 63 >>> [(4, 3), (8, 1)] 64 65 # 答案 66 # [(x,y) for x in range(5) if x%2==0 for y in range(5) if y %2==1] 67 68 # 求M中3,6,9组成的列表M = [[1,2,3],[4,5,6],[7,8,9]] 69 M = [[1,2,3],[4,5,6],[7,8,9]] 70 l = [i[2] for i in M] 71 print(l) 72 >>> [3, 6, 9] 73 74 # 答案 75 # [row[2] for row in M]