一、递归和迭代
1、递归:(问路示例)
2、迭代:简单理解为更新换代( 儿子生孙子的故事)
二、迭代器协议
1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)
2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
4、for循环的本质就是遵循迭代器协议去访问对象,那么for循环的对象肯定都是迭代器。
5、不可迭代对象:字符串,列表,元组,字典,集合,文件对象。只不过通过for循环,调用了他们内部的__iter__方法,把他们变成了可迭代对象。
特点:
1.生成器是可迭代对象
2.实现了延迟计算,看内存(按需,执行)
3.生成器本质和其他类型一样,都是实现了迭代器协议,只不过生成器是一边计算,一边生成,从而节省内存空间,其余的可迭代对象可没有好处。
三、迭代器
ps1:
1、遵循迭代器协议访问方式
x='hello'
# print(dir(x))
iter_test=x.__iter__() print(iter_test)
print(iter_test.__next__()) #获取第1个值
print(iter_test.__next__()) #获取第2个值
print(iter_test.__next__()) #获取第3个值
print(iter_test.__next__()) #获取第4个值
print(iter_test.__next__()) #获取第5个值
print(iter_test.__next__()) #超出边界,当for循环结束时,捕捉到StopIteration异常,他就会终止迭代
执行结果:
Traceback (most recent call last):
h
File "D:/python/day9/iter和yield.py", line 14, in <module>
e
print(iter_test.__next__()) #超出边界,当for循环结束时,捕捉到StopIteration异常,终止迭代
l
StopIteration
l
o
ps2:
2、for循环访问方式
for循环l本质就是遵循迭代器协议的访问方式,先调用diedai_l=l.__iter__()方法,或者直接diedai_l=iter(l),然后依次执行diedai_l.next(),直到for循环捕捉到StopIteration终止循环。
for循环所有对象的本质都是一样的原理。
用for循环的方式
l=[1,2,3]
for i in l: #把列表变成i_l=l.__iter_() ,再执行他下面的i_l.__next__()
print(i)
执行结果:
1
2
3
ps3:
3、用索引的方式,遍历列表的值
l=[1,2,3] index=0
while index < len(l):
print(l[index])
index += 1
执行结果:
1
2
3
ps4:
用迭代器的方式
l=[1,2,3]
iter_l=l.__iter__() #遵循迭代器协议,生成可迭代对象
print(iter_l.__next__()) #取列表的值
print(iter_l.__next__()) #取列表的值
print(iter_l.__next__()) #取列表的值
执行结果:
1
2
3
ps5:
下标访问方式
l = ['a', 'b', 'c']
print(l[0])
print(l[1])
print(l[2])
#print(l[3]) #起出边界报错:IndexError
执行结果:
a
b
c
ps6:
集合的方式
方法一:
#集合的方式
s={1,2,3}
for i in s:
print(i)
方法二:
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异常,终止迭代
执行结果:
<set_iterator object at 0x00BF4198>
Traceback (most recent call last):
1
File "D:/python/day9/iter and yield.py", line 46, in <module>
2
print(iter_s.__next__()) #超出边界,当for循环结束时,捕捉到StopIteration异常,终止迭代
3
StopIteration
ps7:
字典的方式
#字典的方式
dic={'a':1,'b':2}
iter_d=dic.__iter__()
print(iter_d.__next__())
print(iter_d.__next__())
print(iter_d.__next__())
ps8:
文件的方式
1、创建一个test.txt文件,内容如下:
111111
222222
333333
2、执行下面代码
#文件的方式
f=open('test.txt','r+')
#for i in f:
iter_f=f.__iter__() #遵循可迭代原则,转换成迭代器,要的时候拿到内存,可以节约内存空间
print(iter_f)
print(iter_f.__next__(),end='') #第一行
print(iter_f.__next__(),end='') #第二行
print(iter_f.__next__(),end='') #第三行
print(iter_f.__next__(),end='') #执行完了的时候,就捕捉到StopIteration异常,终止迭代
执行结果:
<_io.TextIOWrapper name='test.txt' mode='r+' encoding='cp936'>
Traceback (most recent call last):
111111
File "D:/python/day9/iter and yield.py", line 64, in <module>
222222
print(iter_f.__next__(),end='')
333333StopIteration #执行完了的时候,就捕捉到StopIteration异常,终止迭代
ps9:
用while去模拟for循环做的事情,实现迭代器的过程
l=[1,2,3,4,5]
diedai_l=l.__iter__()
while True:
try:
print(diedai_l.__next__())
except StopIteration:
# print('迭代完毕了,循环终止了')
break #直接break,捕捉到导常,就不会报StopIteration异常
执行结果:
1
2
3
4
5
迭代器总结:
l=['die','erzi','sunzi','chongsunzi'] #把所有结果都放在内存中,比较占用内存 iter_l=l.__iter__() #转成迭代器形式,可以在任意位置传输(也叫可迭代对象)
print(iter_l)
print(iter_l.__next__()) #第一次调用,得到的结果:die
print(iter_l.__next__()) #第二次调用, 得到的结果:erzi
print(iter_l.__next__()) #第三次调用, 得到的结果:sunzi
print(iter_l.__next__()) #第四次调用, 得到的结果:chongsunzi
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 )示例:
def test():
yield 1 #只要有yield就是生成器
yield 2 #他可以yield多次,yield可以保存函数状态
yield 3
g=test()
print('来自函数',g)
print(g.__next__()) #生成器自动实现了迭成器,所以会有__next__()方法。
print(g.__next__()) #运行一次,相当于保存的是上一次内存里状态的结果
print(g.__next__())
执行结果:
来自函数 <generator object(迭代器对象) test at 0x01B0BAE0>
1
2
3
六、补充知识:
三元表达式
#三元表达式演变过程 # name='alex'
# 'SB' if name == 'alex' else '帅哥' #if判断name=alex就,返回SB;如果不等于alex,就返回帅哥。但SB要写在最前面。
七、三元表达式完整写法
if判断name=alex就,返回SB;如果不等于alex,就返回帅哥。但SB要写在最前面。
name='alex'
name='linhaifeng'
res='SB' if name == 'alex' else '帅哥' #三元表达式
print(res)
执行结果:
帅哥
八、生成器表达式和列表解析
ps1:
生成一个列表
egg_list=[]
for i in range(10):
egg_list.append('鸡蛋%s' %i)
print(egg_list)
执行结果:
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
ps2:
列表解析方法(生成列表)
l=['鸡蛋%s' %i for i in range(10)]
print(l)
执行结果:
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
ps3:
三元表达式方法(生成列表)
#鸡蛋>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)
执行结果:
#鸡蛋>5结果:
['鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9'] #鸡蛋<5结果:
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4']
ps4:
生成器表达式(基于迭代器__next__方法进行取值)
laomuji=('鸡蛋%s' %i for i in range(10)) #生成器表达式
print(laomuji) print(laomuji.__next__()) #基于迭代器__next__方法进行取值
print(laomuji.__next__())
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
#print(next(laomuji)) #超出边界,当for循环结束时,捕捉到StopIteration异常,终止迭代
执行结果:
<generator object <genexpr> at 0x010EBAB0>
鸡蛋0
鸡蛋1
鸡蛋2
鸡蛋3
鸡蛋4
鸡蛋5
鸡蛋6
鸡蛋7
鸡蛋8
鸡蛋9
ps5:
其它
l=[1,2,3,34]
#map(func,l) #可迭代对象
print(sum(l)) #求和,使用的是__iter__()方法转换成可迭代对象 #生成100000000
print(sum(list(range(100000000)))) #sum传给生成器生成一个列表
print(sum(i for i in range(10000000000000))) #没有运行结果
执行结果:
40 4999999950000000
ps6:
生成器(生孩子事例)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author: nulige import time
def test():
print('开始生孩子啦.......')
print('开始生孩子啦.......')
print('开始生孩子啦.......')
yield '我' #return
time.sleep(3)
print('开始生儿子啦.......')
yield '儿子' time.sleep(3)
print('开始生孙子啦.......')
yield '孙子' res=test()
print(res)
print(res.__next__())
print(res.__next__())
print(res.__next__())
执行结果:
<generator object test at 0x012EBB40>
开始生孩子啦.......
开始生孩子啦.......
开始生孩子啦.......
我
开始生儿子啦.......
儿子
开始生孙子啦.......
孙子
ps7:
send触发yield返回值原理
#yield 3相当于return 控制的是函数的返回值
#x=yield的另外一个特性,接受send传过来的值,赋值给x def test():
print('开始啦')
firt = yield 1 # return 1,first=None
print('第一次', firt)
yield 2
print('第二次') t = test()
res = t.__next__() # next(t)
print(res)
# t.__next__()
res=t.send(None)
# res = t.send # ('函数停留在first那个位置,我就是给first赋值的')
print(res)
执行结果:
开始啦
1 第一次 None
2
ps8:
吃包子事例
import time
def producer():
ret=[]
for i in range(100):
time.sleep(0.1)
ret.append('包子%s' %i)
return ret def consumer(res):
for index,baozi in enumerate(res):
time.sleep(0.1)
print('第%s个人,吃了%s' %(index,baozi)) res=producer()
consumer(res)
执行结果:
第0个人,吃了包子0
第1个人,吃了包子1
第2个人,吃了包子2
第3个人,吃了包子3
第4个人,吃了包子4
第5个人,吃了包子5
第6个人,吃了包子6
第7个人,吃了包子7
第8个人,吃了包子8
第9个人,吃了包子9
第10个人,吃了包子10
第11个人,吃了包子11
第12个人,吃了包子12
第13个人,吃了包子13
第14个人,吃了包子14
第15个人,吃了包子15
第16个人,吃了包子16
第17个人,吃了包子17
第18个人,吃了包子18
第19个人,吃了包子19
第20个人,吃了包子20
第21个人,吃了包子21
第22个人,吃了包子22
第23个人,吃了包子23
第24个人,吃了包子24
第25个人,吃了包子25
第26个人,吃了包子26
第27个人,吃了包子27
第28个人,吃了包子28
第29个人,吃了包子29
第30个人,吃了包子30
第31个人,吃了包子31
第32个人,吃了包子32
第33个人,吃了包子33
第34个人,吃了包子34
第35个人,吃了包子35
第36个人,吃了包子36
第37个人,吃了包子37
第38个人,吃了包子38
第39个人,吃了包子39
第40个人,吃了包子40
第41个人,吃了包子41
第42个人,吃了包子42
第43个人,吃了包子43
第44个人,吃了包子44
第45个人,吃了包子45
第46个人,吃了包子46
第47个人,吃了包子47
第48个人,吃了包子48
第49个人,吃了包子49
第50个人,吃了包子50
第51个人,吃了包子51
第52个人,吃了包子52
第53个人,吃了包子53
第54个人,吃了包子54
第55个人,吃了包子55
第56个人,吃了包子56
第57个人,吃了包子57
第58个人,吃了包子58
第59个人,吃了包子59
第60个人,吃了包子60
第61个人,吃了包子61
第62个人,吃了包子62
第63个人,吃了包子63
第64个人,吃了包子64
第65个人,吃了包子65
第66个人,吃了包子66
第67个人,吃了包子67
第68个人,吃了包子68
第69个人,吃了包子69
第70个人,吃了包子70
第71个人,吃了包子71
第72个人,吃了包子72
第73个人,吃了包子73
第74个人,吃了包子74
第75个人,吃了包子75
第76个人,吃了包子76
第77个人,吃了包子77
第78个人,吃了包子78
第79个人,吃了包子79
第80个人,吃了包子80
第81个人,吃了包子81
第82个人,吃了包子82
第83个人,吃了包子83
第84个人,吃了包子84
第85个人,吃了包子85
第86个人,吃了包子86
第87个人,吃了包子87
第88个人,吃了包子88
第89个人,吃了包子89
第90个人,吃了包子90
第91个人,吃了包子91
第92个人,吃了包子92
第93个人,吃了包子93
第94个人,吃了包子94
第95个人,吃了包子95
第96个人,吃了包子96
第97个人,吃了包子97
第98个人,吃了包子98
第99个人,吃了包子99
我是[wupeiqi],我准备开始吃包子了
我是[yuanhao_SB],我准备开始吃包子了
wupeiqi 很开心的把【包子 0】吃掉了
yuanhao_SB 很开心的把【包子 0】吃掉了
wupeiqi 很开心的把【包子 1】吃掉了
yuanhao_SB 很开心的把【包子 1】吃掉了
wupeiqi 很开心的把【包子 2】吃掉了
yuanhao_SB 很开心的把【包子 2】吃掉了
wupeiqi 很开心的把【包子 3】吃掉了
yuanhao_SB 很开心的把【包子 3】吃掉了
wupeiqi 很开心的把【包子 4】吃掉了
yuanhao_SB 很开心的把【包子 4】吃掉了
wupeiqi 很开心的把【包子 5】吃掉了
yuanhao_SB 很开心的把【包子 5】吃掉了
wupeiqi 很开心的把【包子 6】吃掉了
yuanhao_SB 很开心的把【包子 6】吃掉了
wupeiqi 很开心的把【包子 7】吃掉了
yuanhao_SB 很开心的把【包子 7】吃掉了
wupeiqi 很开心的把【包子 8】吃掉了
yuanhao_SB 很开心的把【包子 8】吃掉了
wupeiqi 很开心的把【包子 9】吃掉了
yuanhao_SB 很开心的把【包子 9】吃掉了
总结:
1.把列表解析的[]换成()得到的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和。
生产者消费者模型(单线程一边发送,一边执行)
import time
def producer():
ret=[]
for i in range(100):
time.sleep(0.1)
ret.append('包子%s' %i)
return ret def consumer(name):
print('我是[%s],我准备开始吃包子了' %name)
while True:
baozi=yield
time.sleep(1)
print('%s 很开心的把【%s】吃掉了' %(name,baozi)) def producer():
c1=consumer('wupeiqi')
c2=consumer('yuanhao_SB')
c1.__next__()
c2.__next__()
for i in range(10):
time.sleep(1)
c1.send('包子 %s' %i) #发送的值,就是yield的返回值
c2.send('包子 %s' %i)
producer()
执行结果:
我是[wupeiqi],我准备开始吃包子了
我是[yuanhao_SB],我准备开始吃包子了
wupeiqi 很开心的把【包子 0】吃掉了
yuanhao_SB 很开心的把【包子 0】吃掉了
wupeiqi 很开心的把【包子 1】吃掉了
yuanhao_SB 很开心的把【包子 1】吃掉了
wupeiqi 很开心的把【包子 2】吃掉了
yuanhao_SB 很开心的把【包子 2】吃掉了
wupeiqi 很开心的把【包子 3】吃掉了
yuanhao_SB 很开心的把【包子 3】吃掉了
wupeiqi 很开心的把【包子 4】吃掉了
yuanhao_SB 很开心的把【包子 4】吃掉了
wupeiqi 很开心的把【包子 5】吃掉了
yuanhao_SB 很开心的把【包子 5】吃掉了
wupeiqi 很开心的把【包子 6】吃掉了
yuanhao_SB 很开心的把【包子 6】吃掉了
wupeiqi 很开心的把【包子 7】吃掉了
yuanhao_SB 很开心的把【包子 7】吃掉了
wupeiqi 很开心的把【包子 8】吃掉了
yuanhao_SB 很开心的把【包子 8】吃掉了
wupeiqi 很开心的把【包子 9】吃掉了
yuanhao_SB 很开心的把【包子 9】吃掉了 #运行结果省略部分.....