一、递归和迭代:
1、递归(问路的案例)
递归算法是自己调用自己的过程
2、迭代(父生子,子生孙)
更新换代
二、迭代器协议:
1、迭代器协议:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就触发StopTteration异常,已终止迭代(只能往下走,不能往上走)
2、可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
3、协议是一种约定,可迭代对象实现了迭代器协议,Python的内部工具(for循环,sum,max,min函数等)使用迭代器协议访问对象
4、for循环的本质就是遵循迭代器协议取访问对象,那么for循环的对象肯定都是迭代器。
5、不可迭代对象:字符串,列表,元组,字典,集合,文件对象。只不过for循环时,调用了他们的内部__iter__()方法,把他们变成了可迭代对象
-注-
1、生成器是可迭代对象
2、实现了延迟计算,看内存(按需执行)
3、生成器本质和其他类型一样,都是实现了迭代器协议,只不过生成器是一边计算一边生成,从而节省内存空间,其余的可迭代对象没有这个功能
三、迭代器:
1、遵循迭代器协议的访问方式:
1 x='hello' # 非可迭代对象 2 # print(dir(x)) 3 iter_test=x.__iter__() #调用字符串的了iter方法 4 5 print(iter_test) 6 print(iter_test.__next__()) #获取第1个值 ,调用可迭代对象的next方法 7 print(iter_test.__next__()) #获取第2个值 8 print(iter_test.__next__()) #获取第3个值 9 print(iter_test.__next__()) #获取第4个值 10 print(iter_test.__next__()) #获取第5个值 11 print(iter_test.__next__()) #超出边界,当for循环结束时,捕捉到StopIteration异常,他就会终止迭代
2、for循环的访问方式:
for循环 l 本质是遵循迭代器协议的访问方式,先调用l 的__iter__()方法,使其成为一个可迭代对象,在调用next方法直到for循环捕捉到StopIteration异常
1 # l=[1,2,3] 2 # for i in l: #i_l=l.__iter_() i_l.__next__() 3 # print(i)
3、用索引方式遍历列表的值
1 l=[1,2,3,4] 2 3 index=0 4 while index < len(l): 5 print(l[index]) 6 index += 1
4、用迭代器的方式遍历列表
1 l=[1,2,3] 2 iter_l=l.__iter__() #遵循迭代器协议,生成可迭代对象 3 print(iter_l.__next__()) #取列表的值 4 print(iter_l.__next__()) #取列表的值 5 print(iter_l.__next__()) #取列表的值
5、迭代的方式遍历集合
s={1,2,3} iter_s=s.__iter__() #通过iter方法 print(iter_s) print(iter_s.__next__()) #调用next print(iter_s.__next__()) print(iter_s.__next__()) print(iter_s.__next__()) #超出边界,当for循环结束时,捕捉到StopIteration异常,终止迭代
6、迭代的方式遍历字典(默认是key)
dic={'a':1,'b':2} iter_d=dic.__iter__() print(iter_d.__next__()) print(iter_d.__next__()) print(iter_d.__next__())
7、迭代的方式访问文件
1 #文件的方式(创建文件test,txt) 2 f=open('test.txt','r+') 3 for i in f: 4 5 =====》》》 6 7 8 f=open('test.txt','r+') 9 iter_f=f.__iter__() #遵循可迭代原则,转换成迭代器,要的时候拿到内存,可以节约内存空间 10 print(iter_f) 11 print(iter_f.__next__(),end='') #第一行 12 print(iter_f.__next__(),end='') #第二行 13 print(iter_f.__next__(),end='') #第三行 14 print(iter_f.__next__(),end='') #执行完了的时候,就捕捉到StopIteration异常,终止迭代
8、while实现for循环
1 l=[1,2,3,4,5] 2 diedai_l=l.__iter__() 3 while True: 4 try: 5 print(diedai_l.__next__()) 6 except StopIteration: 7 # print('迭代完毕了,循环终止了') 8 break #直接break,捕捉到导常,就不会报StopIteration异常
迭代器总结:
1 l=['die','erzi','sunzi','chongsunzi'] #把所有结果都放在内存中,比较占用内存(所以才会引出生成器) 2 3 iter_l=l.__iter__() #转成迭代器形式,可以在任意位置传输(也叫可迭代对象) 4 print(iter_l) 5 print(iter_l.__next__()) #第一次调用,得到的结果:die 6 print(iter_l.__next__()) #第二次调用, 得到的结果:erzi 7 print(iter_l.__next__()) #第三次调用, 得到的结果:sunzi 8 print(iter_l.__next__()) #第四次调用, 得到的结果:chongsunzi 9 print(iter_l.__next__()) #超出边界,捕捉到StopIteration异常,终止迭代
—注—:
next是python的内置函数
说明:使用next内置函数的next()方法,就是在调用l.__iter__(),下的 __next__()方法
1 l=['die','erzi','sunzi','chongsunzi'] 2 iter_l = l.__iter__() 3 print(next(iter_l)) #next()---->iter_l.__next__() 4 print(next(iter_l)) 5 print(next(iter_l)) 6 print(next(iter_l))
四、生成器
1、生成器:
生成器就是迭代器,可以理解为一种数据类型,这种数据类型实现了迭代器协议,其他的数据类型需要调用自己内置的__iter_方法,所以生成器就是可迭代对象。
2、生成器分类以及在Python中的表现形式:(Python有两种不同的方式提供生成器)
①生成器函数:常规函数定义,但是,使用yield 语句代替return语句,但是,yield在同一层函数中,可以多次使用,一次返回一个结果,返回一个结果后,将函数设置为挂起状态,以便下次从他离开的地方继续执行。
②生成器表达式:类似于列表的推导,但是生成器返回按需产生结果的一个对象,而不是一次性构建一个结果列表。
3、生成器示例之 yield
1 def test(): 2 yield 1 #只要有yield就是生成器 3 yield 2 #他可以yield多次,yield可以保存函数状态 4 yield 3 5 g=test() 6 print('来自函数',g) 7 print(g.__next__()) #生成器自动实现了迭成器,所以会有__next__()方法。 8 print(g.__next__()) #运行一次,相当于保存的是上一次内存里状态的结果 9 print(g.__next__())
4、补充 知识 之 三元表达式:
1 #三元表达式演变过程 2 3 # name='alex' 4 # 'SB' if name == 'alex' else '帅哥' #if判断name=alex就,返回SB;如果不等于alex,就返回帅哥。但SB要写在最前面。
5、生成器之表达式和列表解析
5.1、for循环生成一个列表
1 1 egg_list=[] 2 2 for i in range(10): 3 3 egg_list.append('鸡蛋%s' %i) 4 4 print(egg_list) 5 6 -----> 7 8 1 ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
5.2、列表解析生成一个列表
1 l=['鸡蛋%s' %i for i in range(10)] # 也可以是两元 2 print(l)3 ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
5.3 三元表达式生成列表
#鸡蛋>5 l1=['鸡蛋%s' %i for i in range(10) if i > 5 ] # l1=['鸡蛋%s' %i for i in range(10) if i > 5 else i] #没有四元表达式 print(l1) #鸡蛋<5 l2=['鸡蛋%s' %i for i in range(10) if i < 5] print(l2) -------------> 1 #鸡蛋>5结果: 2 ['鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9'] 3 4 #鸡蛋<5结果: 5 ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4']
5.4、生成器表达式基于迭代器next方法取值
1 laomuji=('鸡蛋%s' %i for i in range(10)) #生成器表达式 2 print(laomuji) 3 4 print(laomuji.__next__()) #基于迭代器__next__方法进行取值 5 print(laomuji.__next__()) 6 print(next(laomuji)) 7 print(next(laomuji)) 8 print(next(laomuji)) 9 print(next(laomuji)) 10 print(next(laomuji)) 11 print(next(laomuji)) 12 print(next(laomuji)) 13 print(next(laomuji)) 14 #print(next(laomuji)) #超出边界,当for循环结束时,捕捉到StopIteration异常,终止迭代
5.5、其他
1 l=[1,2,3,34] 2 #map(func,l) #可迭代对象 3 print(sum(l)) #求和,使用的是__iter__()方法转换成可迭代对象 4 5 #生成100000000 6 print(sum(list(range(100000000)))) # 会很卡,一次性生成一个列表 7 8 #sum传给生成器生成一个列表 9 print(sum(i for i in range(10000000000000))) #没有运行结果
6、生成器示例之 生孩子:
1 import time 2 def test(): 3 print('开始生孩子啦.......') 4 print('开始生孩子啦.......') 5 print('开始生孩子啦.......') 6 yield '我' #return 7 time.sleep(3) 8 print('开始生儿子啦.......') 9 yield '儿子' 10 11 time.sleep(3) 12 print('开始生孙子啦.......') 13 yield '孙子' 14 15 res=test() 16 print(res) 17 print(res.__next__()) # 打印我 就挂起,后面的不在执行 18 print(res.__next__()) 19 print(res.__next__())
7.send触发yield返回值原理
1 #yield 3相当于return 控制的是函数的返回值 2 #x=yield的另外一个特性,接受send传过来的值,赋值给x 3 4 def test(): 5 print('开始啦') 6 firt = yield 1 # return 1,first=None 7 print('第一次', firt) 8 yield 2 9 print('第二次') 10 11 t = test() 12 res = t.__next__() # next(t) 13 print(res) 14 # t.__next__() 15 res=t.send(None) 16 # res = t.send # ('函数停留在first那个位置,我就是给first赋值的') 17 print(res)
8.吃包子
1 import time 2 def producer(): 3 ret=[] 4 for i in range(100): 5 time.sleep(0.1) 6 ret.append('包子%s' %i) 7 return ret 8 9 def consumer(res): 10 for index,baozi in enumerate(res): 11 time.sleep(0.1) 12 print('第%s个人,吃了%s' %(index,baozi)) 13 14 res=producer() 15 consumer(res) 16 17 18 19 ------> 20 21 22 23 24 第0个人,吃了包子0 25 第1个人,吃了包子1 26 第2个人,吃了包子2 27 第3个人,吃了包子3 28 第4个人,吃了包子4 29 第5个人,吃了包子5 30 ....
总结:
1.把列表解析的[]换成()得到的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和。
补充:send使用 类似next
def foo(): print('---') yield 1 print('****') count = yield 2 print(count) a = foo() s1 = a.send(None) # == a.next() ,第一次send时,如果之前没有执行过next,先传入None print(s1) s2 = a.send('aaa') # 先yield 后赋值 print(s2)
来源:https://www.cnblogs.com/JerryZao/p/8634205.html