Python:迭代器+生成器
迭代器和生成器
迭代器
今日内容:https://www.cnblogs.com/liwenzhou/p/9761027.html
器:器具/工具
迭代:其实属于一种重复的过程,迭代中每一次动作和下一次是有一定联系的
for循环就是典型迭代的过程,迭代取值的过程。
1. 可迭代对象(iterable):
只要有__iter__方法,它就是一个可迭代对象。
2. 迭代器(iterator):
调用可迭代对象的__iter__方法,得到的就是一个迭代器。
直白一点说:迭代取值的工具
迭代器对象:必须有一个__next__方法。
调用__next__方法,就能够从中取出一个值
3. 迭代器协议:
1. 在Python内部很多方法或者函数都支持迭代器协议。
一个对象必须要有__next__方法,执行该方法要么返回一个值,要么报出一个StopIteration错误。
4. 迭代器的应用:
range()
1. 当数据非常多的时候, 迭代器就可以不一次性把值都放到内存,而是要一个才计算一个
面试题:python2中range和xrange有什么区别?
range(10) --> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
xrange(10) --> 可迭代对象
Python3中,range(10)默认返回的就是一个可迭代对象。
2. 迭代器的不足:
1. 迭代器取值只能一个一个的取,不能跳着取, 也不能回头取
2. 迭代器的值只能读取一次,读取完就没了。
5. 总结:
1. 常见的可迭代对象:
1. list
2. string
3. dict
4. set
5. tuple
6. range(10)
2. 迭代器对象
打开的文件
3. 为什么要讲可迭代对象和迭代器?
1. 不依赖索引for循环遍历对象
2. 通过迭代器存储大量的数据
4. 得到迭代器的方式
1. 基于一个已经存在的可迭代对象,调用她的__iter__方法
2. 使用Python内置好的一些方法,比如range()
5. 如何自己造一个迭代器?
使用生成器!
生成器
初识生成器
我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行 iter方法得到的,迭代器有的好处是可以节省内存。
如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器。
Python 中提供的 生成器:
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
3.数据转化:list() tuple() 将生成器对象 转化 .一般不这么用.
生成器Generator:
本质:迭代器 ( 所以自带了 __iter__方法和 __next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
生成器函数
一个包含yield关键字的函数就是一个生成器函数。
yield和return一样可以从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,只能返回一次,yield可以返回多次。
调用生成器函数不会得到返回的具体的值,而是得到一个生成器对象。
每一次从这个可迭代对象获取值,就能推动函数的执行,获取新的返回值。直到函数执行结束(yield像是拥有能够让函数暂停的魔力)。
def my_range(): print('我是一个生成器函数') n = 0 while 1: yield n n += 1
生成器有什么好处呢?就是不会一下子在内存中生成太多数据
接下来,我们在这个函数的基础上来写一个我们自己的range函数,实现开始和结束
def my_range2(start, stop): n = start while n < stop: yield n n += 1
再进一步,实现步长:
def my_range3(start, stop, step): n = start while n < stop: yield n n += step
生成器本质上就是个迭代器,我们根据自己的想法创造的迭代器,它当然也支持for循环:
for i in my_range3(1, 10, 2): print(i)
更多应用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def tail(filename): f = open(filename) f.seek(0, 2) #从文件末尾算起 while True: line = f.readline() # 读取文件中新的文本行 if not line: time.sleep(0.1) continue yield line tail_g = tail('tmp') for line in tail_g: print(line)
send
yield可以返回值,也可以接收值。
通过生成器的send方法可以给yield传值。
def eat(name): print('%s要开始吃了!' % name) while 1: food = yield print('{}在吃{}'.format(name, food)) a = eat('alex') a.__next__() # 初始化,让函数暂停在yield处 a.send('包子') # send两个作用:1.给yield传值 2.继续执行函数 a.send('饺子')
yield可以同时返回值和接收值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def averager(): total = 0.0 count = 0 average = None while True: term = yield average total += term count += 1 average = total/count g_avg = averager() next(g_avg) print(g_avg.send(10)) print(g_avg.send(30)) print(g_avg.send(5))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def init(func): #在调用被装饰生成器函数的时候首先用next激活生成器 def inner(*args,**kwargs): g = func(*args,**kwargs) next(g) return g return inner @init def averager(): total = 0.0 count = 0 average = None while True: term = yield average total += term count += 1 average = total/count g_avg = averager() # next(g_avg) 在装饰器中执行了next方法 print(g_avg.send(10)) print(g_avg.send(30)) print(g_avg.send(5)
yield from
在一个生成器中引用另外一个生成器。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def gen1(): for c in 'AB': yield c for i in range(3): yield i print(list(gen1())) def gen2(): yield from 'AB' yield from range(3) print(list(gen2()))
本章小结
可迭代对象:
拥有__iter__方法
特点:惰性运算
例如:range(),str,list,tuple,dict,set
迭代器Iterator:
拥有__iter__方法和__next__方法
例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o
生成器Generator:
本质:迭代器,所以拥有__iter__方法和__next__方法
特点:惰性运算,开发者自定义
使用生成器的优点:
1.延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#列表解析 sum([i for i in range(100000000)])#内存占用大,机器容易卡死 #生成器表达式 sum(i for i in range(100000000))#几乎不占内存
2.提高代码可读性
生成器相关的面试题
生成器在编程中发生了很多的作用,善用生成器可以帮助我们解决很多复杂的问题
除此之外,生成器也是面试题中的重点,在完成一些功能之外,人们也想出了很多魔性的面试题。
接下来我们就来看一看~
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def demo(): for i in range(4): yield i g=demo() g1=(i for i in g) g2=(i for i in g1) print(list(g1)) print(list(g2))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def add(n,i): return n+i def test(): for i in range(4): yield i g=test() for n in [1,10]: g=(add(n,i) for i in g) print(list(g))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import os def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper @init def list_files(target): while 1: dir_to_search=yield for top_dir,dir,files in os.walk(dir_to_search): for file in files: target.send(os.path.join(top_dir,file)) @init def opener(target): while 1: file=yield fn=open(file) target.send((file,fn)) @init def cat(target): while 1: file,fn=yield for line in fn: target.send((file,line)) @init def grep(pattern,target): while 1: file,line=yield if pattern in line: target.send(file) @init def printer(): while 1: file=yield if file: print(file) g=list_files(opener(cat(grep('python',printer())))) g.send('/test1') 协程应用:grep -rl /dir