一、递归和迭代
1、递归:(问路示例)
递归算法是一种直接或者间接地调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
2、迭代:简单理解为更新换代( 儿子生孙子的故事)
二、迭代器协议
1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)
2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
4、for循环的本质就是遵循迭代器协议去访问对象,那么for循环的对象肯定都是迭代器。
5、不可迭代对象:字符串,列表,元组,字典,集合,文件对象。只不过通过for循环,调用了他们内部的__iter__方法,把他们变成了可迭代对象。
特点:
- 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
- 不能随机访问集合中的某个值,只能从头到尾依次访问
- 访问时只能往后走,不能往前退
- 便于循环比较大的数据集合,节省内存
三、迭代器
ps1:
1、遵循迭代器协议访问方式
1 x=‘hello‘ 2 # print(dir(x)) 3 iter_test=x.__iter__() 4 5 print(iter_test) 6 print(iter_test.__next__()) #获取第1个值 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异常,他就会终止迭代
执行结果:
1 Traceback (most recent call last): 2 h 3 File "D:/python/day9/iter和yield.py", line 14, in <module> 4 e 5 print(iter_test.__next__()) #超出边界,当for循环结束时,捕捉到StopIteration异常,终止迭代 6 l 7 StopIteration 8 l 9 o
ps2:
2、for循环访问方式
for循环l本质就是遵循迭代器协议的访问方式,先调用diedai_l=l.__iter__()方法,或者直接diedai_l=iter(l),然后依次执行diedai_l.next(),直到for循环捕捉到StopIteration终止循环
#for循环所有对象的本质都是一样的原理。
1 l=[1,2,3] 2 for i in l: #把列表变成i_l=l.__iter_() ,再执行他下面的i_l.__next__() 3 print(i)
执行结果:
1 1 2 2 3 3
ps3:
3、用索引的方式,遍历列表的值
1 l=[1,2,3] 2 3 index=0 4 while index < len(l): 5 print(l[index]) 6 index += 1
执行结果:
1 1 2 2 3 3
ps4:
用迭代器的方式
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__()) #取列表的值
执行结果:
1 1 2 2 3 3
ps5:
用for循环的方式
1 l=[1,2,3] 2 for i in l: 3 print(i)
执行结果:
1 1 2 2 3 3
ps6:
集合的方式
方法一:
1 #集合的方式 2 s={1,2,3} 3 for i in s: 4 print(i)
方法二:
1 s={1,2,3} 2 iter_s=s.__iter__() #通过iter方法 3 print(iter_s) 4 print(iter_s.__next__()) #调用next 5 print(iter_s.__next__()) 6 print(iter_s.__next__()) 7 print(iter_s.__next__()) #超出边界,当for循环结束时,捕捉到StopIteration异常,终止迭代
执行结果:
1 <set_iterator object at 0x00BF4198> 2 Traceback (most recent call last): 3 1 4 File "D:/python/day9/iter and yield.py", line 46, in <module> 5 2 6 print(iter_s.__next__()) #超出边界,当for循环结束时,捕捉到StopIteration异常,终止迭代 7 3 8 StopIteration
ps7:
字典的方式
1 #字典的方式 2 dic={‘a‘:1,‘b‘:2} 3 iter_d=dic.__iter__() 4 print(iter_d.__next__()) 5 print(iter_d.__next__()) 6 print(iter_d.__next__())
ps8:
文件的方式
1、创建一个test.txt文件,内容如下:
1 111111 2 222222 3 333333
2、执行下面代码
1 #文件的方式 2 f=open(‘test.txt‘,‘r+‘) 3 #for i in f: 4 iter_f=f.__iter__() #遵循可迭代原则,转换成迭代器,要的时候拿到内存,可以节约内存空间 5 print(iter_f) 6 print(iter_f.__next__(),end=‘‘) #第一行 7 print(iter_f.__next__(),end=‘‘) #第二行 8 print(iter_f.__next__(),end=‘‘) #第三行 9 print(iter_f.__next__(),end=‘‘) #执行完了的时候,就捕捉到StopIteration异常,终止迭代
执行结果:
1 <_io.TextIOWrapper name=‘test.txt‘ mode=‘r+‘ encoding=‘cp936‘> 2 Traceback (most recent call last): 3 111111 4 File "D:/python/day9/iter and yield.py", line 64, in <module> 5 222222 6 print(iter_f.__next__(),end=‘‘) 7 333333StopIteration #执行完了的时候,就捕捉到StopIteration异常,终止迭代
ps9:
用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 1 2 2 3 3 4 4 5 5
迭代器总结:
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内置函数
说明: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 die 2 erzi 3 sunzi 4 chongsunzi
生成器(详细讲解)
一、什么是生成器?
生成器就是迭代器,可以理解为一种数据类型,这种类型自动实现了迭代器协议.(其他的数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象。
二、生成器分类及在python中的表现形式?(Python有两种不同的方式提供生成器)
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
三、使用生成器的优点:
Python使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处。
四、生成器小结:
1.是可迭代对象
2.实现了延迟计算,省内存啊
3.生成器本质和其他的数据类型一样,都是实现了迭代器协议,只不过生成器附加了一个延迟计算省内存的好处,其余的可迭代对象可没有这点好处。
五、生成器(yield )示例:
1 def test(): 2 yield 1 #只要有yield就是生成器 3 yield 2 #他可以yield多次 4 yield 3 5 g=test() 6 print(‘来自函数‘,g) 7 print(g.__next__()) #生成器自动实现了迭成器,所以会有__next__()方法。 8 print(g.__next__()) 9 print(g.__next__())
执行结果:
1 来自函数 <generator object(迭代器对象) test at 0x01B0BAE0> 2 1 3 2 4 3
六、补充知识:
三元表达式
1 #三元表达式演变过程 2 3 # name=‘alex‘ 4 # ‘SB‘ if name == ‘alex‘ else ‘帅哥‘ #if判断name=alex就,返回SB;如果不等于alex,就返回帅哥。但SB要写在最前面。
七、三元表达式完整写法
if判断name=alex就,返回SB;如果不等于alex,就返回帅哥。但SB要写在最前面。
1 name=‘alex‘ 2 name=‘linhaifeng‘ 3 res=‘SB‘ if name == ‘alex‘ else ‘帅哥‘ #三元表达式 4 print(res)
执行结果:
1 帅哥
八、生成器表达式和列表解析
ps1:
生成一个列表
1 egg_list=[] 2 for i in range(10): 3 egg_list.append(‘鸡蛋%s‘ %i) 4 print(egg_list)
执行结果:
1 [‘鸡蛋0‘, ‘鸡蛋1‘, ‘鸡蛋2‘, ‘鸡蛋3‘, ‘鸡蛋4‘, ‘鸡蛋5‘, ‘鸡蛋6‘, ‘鸡蛋7‘, ‘鸡蛋8‘, ‘鸡蛋9‘]
ps2:
列表解析方法(生成列表)
1 l=[‘鸡蛋%s‘ %i for i in range(10)] 2 print(l)
执行结果:
1 [‘鸡蛋0‘, ‘鸡蛋1‘, ‘鸡蛋2‘, ‘鸡蛋3‘, ‘鸡蛋4‘, ‘鸡蛋5‘, ‘鸡蛋6‘, ‘鸡蛋7‘, ‘鸡蛋8‘, ‘鸡蛋9‘]
ps3:
三元表达式方法(生成列表)
1 #鸡蛋>5 2 l1=[‘鸡蛋%s‘ %i for i in range(10) if i > 5 ] 3 # l1=[‘鸡蛋%s‘ %i for i in range(10) if i > 5 else i] #没有四元表达式 4 print(l1) 5 6 #鸡蛋<5 7 l2=[‘鸡蛋%s‘ %i for i in range(10) if i < 5] 8 print(l2)
执行结果:
1 #鸡蛋>5结果: 2 [‘鸡蛋6‘, ‘鸡蛋7‘, ‘鸡蛋8‘, ‘鸡蛋9‘] 3 4 #鸡蛋<5结果: 5 [‘鸡蛋0‘, ‘鸡蛋1‘, ‘鸡蛋2‘, ‘鸡蛋3‘, ‘鸡蛋4‘]
ps4:
生成器表达式(基于迭代器__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异常,终止迭代
执行结果:
1 <generator object <genexpr> at 0x010EBAB0> 2 鸡蛋0 3 鸡蛋1 4 鸡蛋2 5 鸡蛋3 6 鸡蛋4 7 鸡蛋5 8 鸡蛋6 9 鸡蛋7 10 鸡蛋8 11 鸡蛋9
ps5:
其它
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))) #没有运行结果
执行结果:
1 40 2 3 4999999950000000
总结:
1.把列表解析的[]换成()得到的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和。