首页 > 代码库 > 迭代器和生成器
迭代器和生成器
迭代的概念:迭代就是可以被遍历的数据类型,也就是可以被一个一个取出来。
那么可迭代的类型有哪些?
from collections import Iterable l = [1,2,3,4] t = (1,2,3,4) d = {1:2,3:4} s = {1,2,3,4} print(isinstance(l,Iterable)) print(isinstance(t,Iterable)) print(isinstance(d,Iterable)) print(isinstance(s,Iterable))
这里可以看出,元组,列表,字典,集合,包括字符串都是可迭代的。
1)为什么可以被迭代呢?
这就要说一下迭代的协议。
迭代的协议就是内部含有——lter——方法。
2)什么是迭代器?
print([1,2].__iter__()) 结果 <list_iterator object at 0x1024784a8>
执行了list([1,2])的__iter__方法,我们好像得到了一个list_iterator,现在我们又得到了一个新名词
——iterator。
iterator的意思就是迭代器。
迭代器和可迭代的对面之间的区别就是在内容里面多了一个:__next__,这也是他们之间的最根本区别。
__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__() print(item)
上路的可迭代对象如果遍历完,那么就会报错。有时候我们就有一些异常的办法把异常处理出去:
l = [1,2,3,4] l_iter = l.__iter__() while True: try: item = l_iter.__next__() print(item) except StopIteration: break
标准格式就是list.__next__(),这之前要看他是不是可迭代对象,如果不行就用__iter__()把他变成可迭代对象,然后在用next一个个遍历.
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
3)range()是迭代器吗?
from collections import Iterator print(isinstance(range(100000000),Iterator)) print(isinstance(range(100),Iterator)) 这里的isinstance是查看里面的值是什么数据类型,Iterator就是看看是不是迭代器。
4)迭代器有什么作用:最重要的一点就是可以节省内存空间。
Python中提供的生成器:
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器Generator:
本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
import time def genrator_fun1(): a = 1 print(‘现在定义了a变量‘) yield a b = 2 print(‘现在又定义了b变量‘) yield b g1 = genrator_fun1() print(‘g1 : ‘,g1) #打印g1可以发现g1就是一个生成器 print(‘-‘*20) #我是华丽的分割线 print(next(g1)) time.sleep(1) #sleep一秒看清执行过程 print(next(g1)) 初识生成器函数
这里有一些注意:首先运行一次genrator_fun1()的话,只会显示他是一个生成器,不会显示别东西,只有当你调用next(genrator_fun1())的话,他才会运行,但是你掉一次next他就只会运行到第一个yield,就不会再运行,只有你一下子调用2个next的时候,他才会一下子运行2个,并且把值返回到genrator_fun 里,下面谁接收的话,就给谁,另外,每一次调用后,当你重新调用的时候,程序又会重新从第一个开始运行。ps:如果yield后面带了东西,当你运行到那的时候才会取出来。
下面是一些例子:
import tima def func(): f=open(filename) f.seak(0,2) while True: line=f.readline() if not line: time.sleep(0.2) continue else: yield line l=func() for line in l: print(line)
#初识生成器二 def produce(): """生产衣服""" for i in range(2000000): 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: #要一批衣服,比如5件 print(i) num +=1 if num == 5: break #到这里我们找工厂拿了8件衣服,我一共让我的生产函数(也就是produce生成器函数)生产2000000件衣服。 #剩下的还有很多衣服,我们可以一直拿,也可以放着等想拿的时候再拿
#初识生成器二 def produce(): """生产衣服""" for i in range(2000000): 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: #要一批衣服,比如5件 print(i) num +=1 if num == 5: break #到这里我们找工厂拿了8件衣服,我一共让我的生产函数(也就是produce生成器函数)生产2000000件衣服。 #剩下的还有很多衣服,我们可以一直拿,也可以放着等想拿的时候再拿
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)) 计算移动平均值(1)
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 的一些应用
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()))
列表表达式和生成器表达式:
之间的差别:列表表达式占系统内存空间较多,生成器几乎不占内存。
list=[x for x in range(10000) ]---列表表达式
list=(x for x in range(10000))---生成器表达式
迭代器和生成器