Python-迭代器和生成器

▼魔方 西西 提交于 2019-12-27 07:25:23

一、递归和迭代:

  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)

 

 

  

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!