python之 迭代器,生成器
什么叫跌代:
可以将某个数据集合内的数据一个一个挨着取出来就叫做跌代。
迭代器协议:
可以被跌代要满足的要求叫做可迭代协议,可迭代对象必须提供一个next的方法,执行该方法要么返回跌代中的下一项,要么就引起一个StopIteration异常,以终止跌代(跌代只能往后走,而不能往前退)
python中的for循环:
for循环的本质就是遵循迭代器协议去访问对象,for循环可以遍历(字符串,列表,元祖,字典,集合,文件对象)这些对象都是不可迭代对象,只不过在for循环时,调用了他们内部的—iter—方法,把他们变成可迭代对象,然后for循环调用可迭代对象的—next—方法去取值,而且for循环会捕捉stoplteration异常,以终止跌代。
#while 循环模拟for循环 diedai_1 = l.__iter__() while True : try:#错误处理机制 print(diedai.__next__()) except StopIteration:#自动捕捉stopiteration错误,防止报错 print("跌代完成") break
for循环为我们提供了一个不依赖索引取值的方法,基于跌代器协议提供了一个统一的可以遍历所有对象的方法,在遍历之前,先调用对象的__iter__方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象都可以通过for循环来遍历了。
什么叫迭代器:
拥有__iter__方法和__next__方法的对象就叫迭代器
l = [1,2,3,4] l_iter = l.__iter__() item = l_iter.__next__()#每次取一个值 print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__()#没有值了报错异常StopIteration print(item)
迭代器的优点:提供一种不依赖索引的取值方式。
惰性计算,节省内存。
迭代器的缺点:取值不如按照索引取值方便。
一次性的,只能往后走,不能往前退。
无法获取长度。
生成器:
什么是生成器:可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象。
Python中提供的生成器:
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器Generator:
本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
生成器函数:
一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中的返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行的结束。
包含yield关键字的函数就叫生成器函数 def genrator_fun1(): a =1 print("现在定义了a变量") yield a b = 2 print("现在又定义了b变量") yield b g1 = genrator_fun1() print(g1) print(next(g1)) print(next(g1))
生成器有什么好处呢,不会一下子在内存中生成太多数据,假如生产一批200000万件的衣服,一批一批的拿,就可以用生成器函数来表示。
def produce(): for i in range(20000): yield "生产了第%s件衣服"%i product_g = produce() print(product_g.__next__())#第一件衣服 print(product_g.__next__())#第二件 print(product_g.__next__())#第三件 num = 0 for i in product_g:#取30件 print(i) num += 1 if num == 30: break
更多应用
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 tail_g = tail("tmp")#“tmp”文件名 for line in tail_g: print(line)
#模拟平均工资 def average(): total = 0 day = 0 average = None while True: term = yield average#相当于return了一个average total += term#总工资等于这几天所有工资的和 day += 1#天数过一天就加一 average = total/day avg = average() next(avg)#这里是激活生成器,avg.send()什么值都不传,和next的效果一样 print(avg.send(10))#send有两个作用,传值和寻找下一个yield(next) print(avg.send(20))
生成器想要运行,必须在调用的时候用next()方法来激活它,那么能不能不激活直接拿来就可以用呢,我们需要给生成器加上一个装饰器就可以解决这个问题了。
def init(func): def inner(*args,**kwargs): g = func(*args,**kwargs)#将函数average传入func()用g来接受返回值 next(g)#在这里激活了生成器 return g return inner @init def average(): total = 0 day = 0 average = None while True: term = yield average total += term day += 1 average = total/day avg = average() #next(avg),用装饰器执行了next方法 print(avg.send(10)) print(avg.send(100)) print(avg.send(150))
yield from
def gen1(): for c in "AB": yield c for i in range(3): yield i print(list(gen1()))#['A', 'B', 0, 1, 2] def gen2(): yield from "AB" yield from range(3) print(list(gen2()))#['A', 'B', 0, 1, 2]
列表推导式和生成器解析式
egg_list = ["鸡蛋%s"% i for i in range(10)] print(egg_list)#列表解析式 laomuji = ("鸡蛋%s"% i for i in range(10)) print(laomuji)#生成器表达式,就是将列表解析式的中括号[]换成()
列表解析式的[]换成()就是生成器表达式,列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式比列表解析式更节省内存空间。python不但使用迭代器协议,让for循环变得更加通用,大部分内置函数,也是使用迭代器协议访问对象的。
总结:
可迭代对象:拥有__iter__()方法,特点就是惰性计算,节省内存。
迭代器:Iterator 拥有__iter__方法和__next__方法
生成器:Generator 本质即使迭代器 特点:延迟计算,一次返回一个结果,这对于大量的数据处理,将会非常有用。