生成器
前言
使用列表推导的前提是将数据全部加载到内存,若是数据量太大的时候,比如有几十万条,那么占用内存和使用率会特别大,但是若是在整个数据集中只使用很少部分,那么其他空间就浪费掉了,生成器使列表解析得到加强,可以在循环的时候动态生成下一个元素。
1.创建生成器
创建生成器最简单的方法就是将列表的推导式改成小括号
list1 = [1,2,3,4,5,6,7,8,9,10]
data = [i for i in list1 if i % 2 == 0]
print('推导式获取偶数值:',data)
data = (i for i in list1 if i % 2 == 0)
print('[]变成()得到的生成器:',data)
print("获取生成器中第1个值",next(data))
print("获取生成器中第2个值",data.__next__())
推导式获取偶数值: [2, 4, 6, 8, 10]
[]变成()得到的生成器: <generator object <genexpr> at 0x00000271E03C9148>
获取生成器中第1个值 2
获取生成器中第2个值 4
这个列子,生成的最后结果是一次性得到的,使用生成器得到的结果是每次调用next或者__next__的魔法方法临时生成的,这就是为什么生成器能够应对大量数据的列表迭代的原因。
在使用next的方式获取值,在生成最后一个元素的时候,继续调用next,会触发异常。
print("获取生成器的第1个值:",next(data))
print("获取生成器的第2个值:",data.__next__())
print("获取生成器的第1个值:",next(data))
print("获取生成器的第1个值:",next(data))
print("获取生成器的第1个值:",next(data))
print("获取生成器的第1个值:",next(data))
获取生成器的第1个值: 6
获取生成器的第2个值: 8
获取生成器的第1个值: 10
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-10-faed4bd43b80> in <module>
2 print("获取生成器的第2个值:",data.__next__())
3 print("获取生成器的第1个值:",next(data))
----> 4 print("获取生成器的第1个值:",next(data))
5 print("获取生成器的第1个值:",next(data))
6 print("获取生成器的第1个值:",next(data))
StopIteration:
解决方式,可以使用for循环
list1 = [1,2,3,4,5,6,7,8,9,10]
data = [i for i in list1 if i % 2 == 0]
for i in data:
print('当前元素是:',i)
当前元素是: 2
当前元素是: 4
当前元素是: 6
当前元素是: 8
当前元素是: 10
2.yield关键字
yield关键字可以创建生成器,调用get_list方法得到的是一个生成器实例,在get_list方法中,使用yield返回i值。m
list2 = [1,2,3,4,5,6,7,8,9,10]
def get_list():
for i in list2:
if i % 2 == 0:
print('当前元素为:',i)
yield i
gen = get_list()
for j in gen:
print('当前获取的值:',j)
print('--'*30)
当前元素为: 2
当前获取的值: 2
------------------------------------------------------------
当前元素为: 4
当前获取的值: 4
------------------------------------------------------------
当前元素为: 6
当前获取的值: 6
------------------------------------------------------------
当前元素为: 8
当前获取的值: 8
------------------------------------------------------------
当前元素为: 10
当前获取的值: 10
------------------------------------------------------------
整个循环的逻辑
每次执行到yield,退出get_list方法,然后执行第13行代码,当本次循环结束是,又到达yield退出的位置,接着完成自身循环。
3.将值传到生成器
生成器可以将方法内部的值传出来,也可以从外部将值传递到生成器。send与next方法都会触发生成器的执行,不同的是send方法可以传递参数到生成器,示例中"while True"会使生成器一直运行,若要终止,需要调用close方法
def get_list():
count = 0
while True:
print('---------------分割线----------------')
print('本次循环开始,count初始值:',count)
outer = yield count
print('<-------生成器从外部接受到的数据:',outer)
count += 1
print('本次循环结束,count值为:',count,'\n')
gen = get_list()
val = next(gen)
print('------>外部调用next从生成器获取到的值:',val)
print()
val = gen.send(20)
print('------>外部从生成器获取到的值:',val)
gen.close
---------------分割线----------------
本次循环开始,count初始值: 0
------>外部调用next从生成器获取到的值: 0
<-------生成器从外部接受到的数据: 20
本次循环结束,count值为: 1
---------------分割线----------------
本次循环开始,count初始值: 1
------>外部从生成器获取到的值: 1
<function generator.close>
迭代器
迭代器可以使对象像列表、字典一样进行迭代,列表使用索引计数来实现元素的逐个遍历,迭代器封装后的对象可以调用next方法来逐个遍历,同样也支持for循环
实际上,能用next函数获取的下一个值的对象都是迭代器,生成器其实也是迭代器的实例,在数据获取完毕后继续使用next函数,同样会出发StopIteration异常,创建迭代器的方法并不复杂,使用iter函数可以将列表、字典转为迭代器。
list3 = [1,2,3,4,5,6,7,8,9,10]
itor_list = iter(list3)
print('使用next获取元素:',next(itor_list))
print('使用__next__获取元素:',itor_list.__next__())
for i in itor_list:
print('获取当前元素:',i)
使用next获取元素: 1
使用__next__获取元素: 2
获取当前元素: 3
获取当前元素: 4
获取当前元素: 5
获取当前元素: 6
获取当前元素: 7
获取当前元素: 8
获取当前元素: 9
获取当前元素: 10