python基础15上_迭代器_生成器
# 迭代器和生成器 # 迭代器: # 双下方法 : 很少直接调用的方法。一般情况下,是通过其他语法触发的 # 可迭代的 —— 可迭代协议 含有__iter__的方法('__iter__' in dir(数据)) # 可迭代的一定可以被for循环 # 迭代器协议: 含有__iter__和__next__方法 # 迭代器一定可迭代,可迭代的通过调用iter()方法就能得到一个迭代器 # 迭代器的特点: # 很方便使用,且只能取所有的数据取一次 # 节省内存空间 # 生成器 # 生成器的本质就是迭代器 # 生成器的表现形式 # 生成器函数 # 生成器表达式 # 生成器函数: #含有yield关键字的函数就是生成器函数 #特点: #调用函数的之后函数不执行,返回一个生成器 #每次调用next方法的时候会取到一个值 #直到取完最后一个,在执行next会报错
#!/usr/bin/env python # coding:utf-8 # 迭代器即迭代的工具,那什么是迭代呢? # 迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值 # while True: # 只是单纯地重复,因而不是迭代 # print('===>') l = [1, 2, 3] count = 0 while count < len(l): # 迭代 print(l[count]) count += 1 ### for 循环就是基于迭代器协议去工作。 # a = l.__iter__() # 转为可迭代对象 # print(a.__next__()) # 使用可迭代对象的next方法 # print(a.__next__()) # print(a.__next__()) # for i in l: # print(i) indx = 0 while indx < len(l): print(l[indx]) indx += 1 ''' #1、为何要有迭代器? 对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。 但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器 #2、什么是可迭代对象? 可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下 'hello'.__iter__ (1,2,3).__iter__ [1,2,3].__iter__ {'a':1}.__iter__ {'a','b'}.__iter__ open('a.txt').__iter__ #3、什么是迭代器对象? 可迭代对象执行obj.__iter__()得到的结果就是迭代器对象 而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象 文件类型是迭代器对象 open('a.txt').__iter__() open('a.txt').__next__() #4、注意: 迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象 ''' dic = {'a': 11, 'b': 22, 'c': 33} iter_dic = dic.__iter__() # 得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身 iter_dic.__iter__() is iter_dic # True # print(iter_dic.__iter__() is iter_dic) # True """ print(iter_dic.__next__()) # 等同于next(iter_dic) print(iter_dic.__next__()) # 等同于next(iter_dic) print(iter_dic.__next__()) # 等同于next(iter_dic) # print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志 # 有了迭代器,我们就可以不依赖索引迭代取值了 """ iter_dic = dic.__iter__() while 1: try: k = next(iter_dic) print(dic[k]) except StopIteration: break # 这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看 for 循环 #基于for循环,我们可以完全不再依赖索引去取值了 dic={'a':111,'b':222,'c':333} for k in dic: print(dic[k]) ''' #for循环的工作原理 #1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic #2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码 #3: 重复过程2,直到捕捉到异常StopIteration,结束循环 '''
#!/usr/bin/env python # coding:utf-8 # 生成器函数, 函数内部有yield ,保留函数的生成状态 def test(): yield 1 yield 2 yield 3 # 相当于return,但是却可以多次 g = test() print(g) print(g.__next__()) # 1 print(g.__next__()) # 2 ### 三元操作符 name = 'alex' res = 'sb' if name =='alex' else 'kkk' print(res) ### 列表解析l # egg_list=[] # for i in range(10): # egg_list.append('鸡蛋%s' %i) # print(egg_list) lis = ['鸡蛋%s' %i for i in range(10)] # 等同于上面的过程。 lis2 = ['鸡蛋%s' %i for i in range(20) if i >10] lis3 = ['鸡蛋%s' %i for i in range(10) if i > 5] # print(lis) print(lis2) print(lis3) ## 例:找到嵌套列表中名字含有两个'e'的所有名字 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] list3 = [name for lst in names for name in lst if name.count('e') >= 2] print(list3) ## 生成器表达式 ## 优点:省内存,一次只产生一个值在内存中 因为生成器表达式是基于迭代器 __next__ 方法取值 . ## 把列表推导式的 [] 换成 () 就是生成器表达式 lise = ('鸡蛋%s' %i for i in range(10)) print(lise) print(lise.__next__()) print(lise.__next__()) print(next(lise)) # next 就是调用了内置的 __next__ # sum 里面可以直接写生成器表达式,因为就是调用了可迭代对象的__next__ print(sum(i for i in range(10000001))) # 一千万
#!/usr/bin/env python # coding:utf-8 import time def test(): print('start 1st...') yield 'first generation.' # 遇到yield就返回结果,并保留运行状态。 time.sleep(3) print('start 2nd___') yield 'second generation..' time.sleep(3) print('start 3rd') yield 'third generation...' a = test() print(a.__next__()) # 遇到yield就返回结果,并保留运行状态。 print(a.__next__()) # 继续上次的运行状态往下执行。 print(a.__next__()) """ 1. 迭代器协议: 对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就StopIteration异常,以终止迭代 2. 可迭代对象: 实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法) 3. 生成器就是自动实现了迭代器协议. """
假设有个字典格式的文件people 内容如下:
{'name':'上海','population':999}
{'name':'杭州','population':8888}
{'name':'苏州','population':7767}
{'name':'无锡','population':23456}
#!/usr/bin/env python # coding:utf-8 ### 把函数做成生成器的好处是,next一次取一个值,立刻就可以操作.不必等到下一个值产生. ### 得到一个值立马可以处理, next中可以加一些操作. def baozi(): for i in range(100): print("正在生产包子...") yield '一笼包子%s' %i print('收钱.') a = baozi() bz1=a.__next__() print(bz1) bz2=a.__next__() print(bz2) ### 母鸡下蛋的传说: def xiadan(): for i in range(100): yield '鸡蛋%s' %i alex = xiadan() print('路人甲获得%s' %alex.__next__()) print('路人乙获得%s' %alex.__next__()) ## 生成器也可以被 for 循环 # for i in alex: # print(i) # import this # 保证代码可读性的前提下,尽量精简 ## 取各地区人口的示例: def get_population(): with open('people','r',encoding='utf-8') as f: for i in f: yield eval(i) g = get_population() # 得到生成器 all_pop = sum(i['population'] for i in g) ## 求和总人口 print(all_pop) ## 如果使用next来取得列表, 也就没有必要了.没有发挥生成器的优势.
一个生动的例子: 生产者消费者模型:
#!/usr/bin/env python # coding:utf-8 import time # def producer(): # ret=[] # for i in range(100): # time.sleep(0.01) # ret.append('包子%s' %i) # # return ret # # def consumer(res): # for idx, things in enumerate(res): # time.sleep(0.1) # print('第%s个人,吃了%s' %(idx,things)) # # # res = producer() # consumer(res) #### 生产者 消费者模型 #### 使用生成器改写上面的程序,实现并发: def consumer(name): print('我是%s, 我要开始吃包子了.' % name) while True: baozi=yield time.sleep(0.5) print('%s 很开心地把 %s 吃掉了.' %(name,baozi)) def producer(): c1 = consumer('Tom') # 调用上面的生成器函数 c1.__next__() for i in range(10): time.sleep(0.5) c1.send('肉包子%s' %i) # 由send producer() ## 单线程并发