一、什么是迭代器协议
1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)
2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
二,为什么要用迭代器
优点:
1:迭代器提供了一种不依赖于索引的取值方式,这样就可以遍历那些没有索引的可迭代对象了(字典,集合,文件)
2:迭代器与列表比较,迭代器是惰性计算的,更节省内存
缺点:
1:无法获取迭代器的长度,使用不如列表索引取值灵活
2:一次性的,只能往后取值,不能倒着取值
1 l=['a','b','c','d','e'] 2 i=l.__iter__() 3 while True: 4 try: 5 print(next(i)) 6 except StopIteration: 7 break
1 d={'a':1,'b':2,'c':3} 2 d.__iter__ 3 for k in d: #d.__iter__() 4 print(k)
1 s={1,2,3,4} 2 for i in s: 3 print(i) 4 5 with open('a.txt','r') as f: 6 for line in f: 7 print(line) 8 9 f=open('a.txt','r') 10 f.__next__ 11 f.__iter__ 12 print(f) 13 print(f.__iter__()) 14 15 for line in f: #f.__iter__() 16 print(line) 17 18 i=f.__iter__() 19 20 while True: 21 try: #异常排查,与StopIteration一起用 22 print(next(i)) 23 except StopIteration: #异常排查,与try一起用 # except 排除 24 break
1 l=[1,2,3] 2 print(len(l)) 3 i=l.__iter__() 4 5 print(next(i)) 6 print(next(i)) 7 print(next(i)) 8 print(next(i)) 9 10 11 for x in i: 12 print(x) 13 for x in i: 14 print(x) 15 for x in i: 16 print(x) 17 for x in i: 18 print(x)
1 查看可迭代对象与迭代器对象 2 3 from collections import Iterable,Iterator 4 5 s='hello' 6 l=[1,2,3] 7 t=(1,2,3) 8 d={'a':1} 9 set1={1,2,3,4} 10 f=open('a.txt') 11 12 都是可迭代的 13 s.__iter__() 14 l.__iter__() 15 t.__iter__() 16 d.__iter__() 17 set1.__iter__() 18 f.__iter__() 19 print(isinstance(s,Iterable)) 20 print(isinstance(l,Iterable)) 21 print(isinstance(t,Iterable)) 22 print(isinstance(d,Iterable)) 23 print(isinstance(set1,Iterable)) 24 print(isinstance(f,Iterable)) 25 26 查看是否是迭代器 27 print(isinstance(s,Iterator)) 28 print(isinstance(l,Iterator)) 29 print(isinstance(t,Iterator)) 30 print(isinstance(d,Iterator)) 31 print(isinstance(set1,Iterator)) 32 print(isinstance(f,Iterator))
三、生成器
什么是生成器:
可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象.
from collections import Iterator 生成器就是一个函数,这个函数内包含有yield这个关键字 。◕‿◕。
生成器分类及在python中的表现形式:(Python有两种不同的方式提供生成器)
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行.
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表.
为何使用生成器:
优点:
Python使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处。
生成器小结:
1.是可迭代对象
2.实现了延迟计算,省内存
3.生成器本质和其他的数据类型一样,都是实现了迭代器协议,只不过生成器附加了一个延迟计算省内存的好处,其余的可迭代对象可没有这点好处,记住喽!!!
生成器与return的区别:return只能返回一次函数就彻底结束了,而yield能返回多次值
yield功能:
1.yield把函数变成生成器-->生成器本质就是迭代器,相当于把__iter__和__next__方法封装到函数内部
2.用return返回值能返回一次,而yield返回多次
3.函数暂停以及继续运行的状态是通过yield保存的
插一嘴:
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
1 >>> L = [x * x for x in range(10)] 2 >>> L 3 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 4 >>> g = (x * x for x in range(10)) 5 >>> g 6 <generator object <genexpr> at 0x1022ef630>
创建L
和g
的区别仅在于最外层的[]
和()
,L
是一个list,而g
是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
如果要一个一个打印出来,可以通过next()
函数获得generator的下一个返回值:
1 >>> next(g) 2 0 3 >>> next(g) 4 1 5 >>> next(g) 6 4 7 >>> next(g) 8 9 9 >>> next(g) 10 16 11 >>> next(g) 12 25 13 >>> next(g) 14 36 15 >>> next(g) 16 49 17 >>> next(g) 18 64 19 >>> next(g) 20 81 21 >>> next(g) 22 Traceback (most recent call last): 23 File "<stdin>", line 1, in <module> 24 StopIteration
我们讲过,generator保存的是算法,每次调用next(g)
,就计算出g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
生成器本质就是迭代器
1 def func(): 2 n=0 3 while True: 4 yield n 5 n+=1 6 7 g=func() 8 9 res=next(g) 10 res=next(g) 11 12 for i in g: 13 pass
yield的表达式形式:
1 food=yield 2 3 def eater(name): 4 print('%s start to eat' %name) 5 while True: 6 food=yield 7 print('%s eat %s' %(name,food)) 8 9 e=eater('zhejiangF4')
e.send与next(e)的区别
1.如果函数内yield是表达式形式,那么必须先next(e)
2.二者的共同之处是都可以让函数在上次暂停的位置继续运行,不一样的地方在于send在触发下一次代码的执行时,会顺便给yield传一个值
1 def test(): 2 print('one') 3 yield 1 #return 1 4 print('two') 5 yield 2 #return 2 6 print('three') 7 yield 3 #return 2 8 print('four') 9 yield 4 #return 2 10 print('five') 11 yield 5 #return 2 12 13 g=test() 14 print(g) 15 # print(isinstance(g,Iterator)) 16 g.__iter__() 17 g.__next__() 18 19 res=next(g) 20 print(res) 21 22 res=next(g) 23 print(res) 24 25 res=next(g) 26 print(res) 27 28 res=next(g) 29 print(res) 30 31 for i in g: 32 print(i)
1 def func(): 2 n=0 3 while True: 4 yield n 5 n+=1 6 7 f=func() 8 print(next(f))
1 import time 2 def tail(file_path): 3 with open(file_path,'r') as f: 4 f.seek(0,2) 5 while True: 6 line=f.readline() 7 if not line: 8 time.sleep(0.3) 9 continue 10 else: 11 # print(line) 12 yield line 13 tail('/tmp/a.txt')
1 def countdown(n): 2 print('start coutdown') 3 while n > 0: 4 yield n #1 5 n-=1 6 print('done') 7 8 g=countdown(5) 9 print(g) 10 11 print(next(g)) 12 print(next(g)) 13 print(next(g)) 14 print(next(g)) 15 print(next(g)) 16 print(next(g)) 17 18 for i in g: #iter(g) 19 print(i) 20 21 while True: 22 try: 23 print(next(g)) 24 except StopIteration: 25 break
四、协程函数
定义:如果在一个函数内部yield的使用方式是表达式形式的话,如x=yield,那么该函数成为协程函数 。◕‿◕。
#例子
1 def eater(name): 2 print('%s start to eat food' %name) 3 food_list=[] 4 while True: 5 food=yield food_list 6 print('%s get %s ,to start eat' %(name,food)) 7 food_list.append(food) 9 print('done') 10 e=eater('钢蛋') 11 print(e) 12 print(next(e)) 13 print(e.send('包子')) 14 print(e.send('韭菜馅包子')) 15 print(e.send('大蒜包子'))
为协程函数添加装饰器:
1 def timmer(func): 2 def wrapper(*args,**kwargs): 3 e=func(*args,**kwargs) 4 next(e) 5 return e 6 return wrapper 7 8 @timmer #eater=timmer(eater) 9 def eater(name): 10 print('%s start to eat food' %name) 11 food_list=[] 12 while True: 13 food=yield food_list 14 print('%s get %s ,to start eat' %(name,food)) 15 food_list.append(food) 16 e = eater('egon') #wrapper('egon') 17 # print(e) 18 # next(e) 19 print(e.send('包子')) 20 print(e.send('包子')) #以下每send一次都会执行一次结果 21 print(e.send('包子')) 22 print(e.send('包子'))
补充:
1 def init(func): 2 def wrapper(*args,**kwargs): 3 res=func(*args,**kwargs) 4 next(res) 5 return res 6 return wrapper 7 8 @init #eater=init(eater) 9 def eater(name): 10 print('%s start to eat' % name) 11 food_list=[] 12 while True: 13 food = yield food_list 14 print('%s eat %s' % (name, food)) 15 food_list.append(food) 16 17 e = eater('zhejiangF4') #wrapper('zhengjiangF4') 18 # print(e) 19 # next(e) #e.send(None) 20 21 print(e.send('123')) 22 print(e.send('123')) 23 print(e.send('123')) 24 print(e.send('123')) 25 print(e.send('123'))
五、协程函数的应用
面向过程的编程思想:流水线式的编程思想,在设计程序时,需要把整个流程设计出来
优点: 1:体系结构更加清晰
2:简化程序的复杂度
缺点:可扩展性极其的差,所以说面向过程的应用场景是:不需要经常变化的软件,如:linux内核,httpd,git等软件
旁白: 此实例包含了装饰器、迭代器、生成器、协程函数、os模块中walk命令,需要好好记住! ♥♥♥♥♥。◕‿◕。♥♥♥♥♥
1 #grep -rl 'python' C:egon 2 import os,time 3 def init(func): 4 def wrapper(*args,**kwargs): 5 res=func(*args,**kwargs) 6 next(res) 7 return res 8 return wrapper 9 10 #找到一个绝对路径,往下一个阶段发一个 11 @init 12 def search(target): 13 '找到文件的绝对路径' 14 while True: 15 dir_name=yield #dir_name='C:\egon' 16 print('车间search开始生产产品:文件的绝对路径') 17 time.sleep(2) 18 g = os.walk(dir_name) 19 for i in g: 20 # print(i) 21 for j in i[-1]: 22 file_path = '%s\%s' % (i[0], j) 23 target.send(file_path) 24 25 @init 26 def opener(target): 27 '打开文件,获取文件句柄' 28 while True: 29 file_path=yield 30 print('车间opener开始生产产品:文件句柄') 31 time.sleep(2) 32 with open(file_path) as f: 33 target.send((file_path,f)) 34 35 @init 36 def cat(target): 37 '读取文件内容' 38 while True: 39 file_path,f=yield 40 print('车间cat开始生产产品:文件的一行内容') 41 time.sleep(2) 42 for line in f: 43 target.send((file_path,line)) 44 45 @init 46 def grep(pattern,target): 47 '过滤一行内容中有无python' 48 while True: 49 file_path,line=yield 50 print('车间grep开始生产产品:包含python这一行内容的文件路径') 51 time.sleep(0.2) 52 if pattern in line: 53 target.send(file_path) 54 55 @init 56 def printer(): 57 '打印文件路径' 58 while True: 59 file_path=yield 60 print('车间printer开始生产产品:得到最终的产品') 61 time.sleep(2) 62 print(file_path) 63 64 65 g=search(opener(cat(grep('python',printer())))) 66 g.send('C:\egon') 67 g.send('D:\dir1') 68 g.send('E:\dir2')