迭代器和生成器
迭代器
可迭代对象:
字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。
两种方式证明可迭代对象:
# 1 - 使用方法看对象有没有__iter__() 方法 ls = [1,2,3] print(dir(ls)) #使用dir() 查看对象都有什么方法 # 2 - 使用模块 from collections import Iterable # isinstance()判断哪个对象是什么类型 print(isinstance(ls,Iterable)) # True
可迭代协议
可以被迭代要满足的要求就是可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。
总结:
可以被for循环的都是可迭代的。
可迭代的:内部必须含有一个__iter__方法
迭代器(iterator)
l = [1,2,3,4] l_iter = l.__iter__() #将可迭代的转化为迭代器 item = l_iter.__next__() #取出第一个值 print(item) # 1 item = l_iter.__next__() #取出第二个值 print(item) # 2
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法
for 循环内部:
将可迭代对象转化为迭代器(可迭代对象.__iter__())
内部使用__next__()方法,依次取值
加了异常处理功能,取值到底后自动停止。
用while循环模拟for循环:
# 用while循环模拟for循环 l = [1,2,3,4] i_iter = l.__iter__() while True: try: item = i_iter.__next__() print(item) except StopIteration: break
总结:
for 循环就是基于迭代器协议提供了一个统一的可遍历所有对象的方法,即在遍历之前,先调用对象的__iter__方法将其转成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象就可以通过for循环来遍历了。
最重要的一点,转化成迭代器,在循环时,同一时刻在内存中只出现一条数据,极大限度的节省了内存。
生成器
初始生成器(Generator)
我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处就是可以节省内存。
如果在某些情况下,需要自己写,我们自己写的这个能实现迭代器功能的东西就叫生成器。
本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
Python中提供的生成器:
1、生成器函数:常规函数定义,但是,使用yield返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。
2、生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。
生成器函数
一个包含yield关键字的函数就是一个生成器函数。
yield可以为我们从函数中返回值,
调用生成器函数不会得到返回的具体的值,而是一个可迭代对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值,指导函数执行结束。
#生成器函数 def gene_func(): yield 1 yield 2 yield 3 # print(gene_func()) # 不会执行函数 得到一个可迭代对象 <generator object gene_func at 0x1040fbba0> ge_object = gene_func() a = ge_object.__next__() print(a) # 1 print(next(ge_object)) # 2
生成器:不会在内存中一下子生成太多数据。要多少就执行多少__next__()
Send方法
send()方法在获取下一个值的时候,给上一个yield的位置传递一个参数
def gene_func(): send_fc1 = yield 1 print('send_fc1 : %s'%send_fc1) #赋值 yield 2 ge_obj = gene_func() ret = ge_obj.__next__() # 1 print('第一个yield返回的值:%s'%ret) ret1 = ge_obj.send('赋值') # 2 print('send赋值:%s'%ret1)
send获取下一个值的效果和next基本一致,只是在获取下一个值得时候,给上一个yield的位置传递一个数据。
注意:
第一次使用生成器的时候,因为没有上一个yield,用next获取下一个值
最后一个yield不能接受外部的值。
def add(n, a_i): return n + 1 def test(): for r_i in range(4): yield r_i g = test() for n in [1,10]: g = (add(n, i) for i in g) # # 第一次: g = ((add(n,i),add(n,i),add(n,i),add(n,i)) 没有调用,不执行 n不赋值 # 第二次:g = (add(n,add(n,i)),add(n,add(n,i)),add(n,add(n,i)),add(n,add(n,i))) print(list(g)) ''' 每个生成器只能取一次 只能前进不能后退 生成器在不找他要值得时候始终不执行 当他执行的时候,要以执行时候的所有变量为准 '''