代码改变世界

生成器generator

2019-07-25 23:00  风e逸  阅读(162)  评论(0)    收藏  举报

生成器:  

  通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

  所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator

  生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。

  生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器。

为什么用生成器:

 

  1.生成器可以一边计算,一边循环,因此可以省去列表的储存空间。归根结底,为了节省储存空间。

  2.生成器可以暂停函数返回值,因此可以生成一个值,然后对这个值进行操作,然后在继续生成值,继续操作。比较便利。生成器的定义:

  第一种方法,与定义列表相似,只是将[ ],改为 ( )。

1 # 生成列表:
2 list1 = [x for x in range(5)]
3 print(list1)
4 # 生成器:
5 ge_list1 = (x for x in range(5))
6 print(next(ge_list1))
7 print(next(ge_list1))
8 print(next(ge_list1))
9 # 直接print(ge_list1) 返回结果是一个生成器<generator object <genexpr> at 0x00000000025C4660>

  第二种方法,def():  yeild

 1 import time
 2 def scq():
 3 
 4     print('---准备生孩子---')
 5     print('---准备生孩子---')
 6     yield ''
 7     time.sleep(3)
 8     print('---准备生孩子---')
 9     yield '儿子'
10     time.sleep(3)
11     print('---准备生孩子---')
12     yield '孙子'
13 req = scq()
14 print(req.__next__())
15 print(req.__next__())
16 print(req.__next__())

 生成器的复杂处理:

 1 # 新建一个文件,名字叫 人口普查
 2 # 内容为:
 3 # {'name': '北京', 'popu': 10}
 4 # {'name': '黑龙江', 'popu': 100000000}
 5 # {'name': '南京', 'popu': 111111111111}
 6 
 7 
 8 def get_po():
 9     with open('人口普查','r',encoding='utf-8') as f: # 文件操作
10         for i in f:
11             yield i
12 
13 g = get_po()
14 s1 = eval(g.__next__()) # 执行起来的生成器返回值并不是字典形式,只是输出显示像是字典,要想变成字典进行关键字取值,需要用eval()转换
15 # print(s1['popu'])
16 # print(s1['name']) # 字典取值时,因为s1保存了运行第一次的值,因此s1其实内容就是字典{'name': '北京', 'popu': 10},因此本次取值返回结果为:北京 
17 # print(g.__next__()) 本次取值是从第二条开始,因为s1中已经next过一次。
18 s2 = eval(g.__next__()) # 接下来可以正常取s2中的值
19 # 求总人口数
20 all_popu = sum(eval(i)['popu'] for i in g) # 先取出i的值,然后对i的值进行处理
21 print(all_popu)

 生成器的另一个运行命令: send(value)

  send(value),表示将value值返回给yield,并有yield赋值给接收的变量

 1 def test():
 2     print('开始制作')
 3     f = yield 1
 4     print('第一次', f)
 5     s=yield 2
 6     print('第二次',s)
 7 
 8 res = test()
 9 print(res.__next__())
10 print(res.send(5))
11 
12 输出结果:
13 开始制作
14 1
15 第一次 5
16 2
17 
18 # 需要注意的是,由于send()输入的值由第一次的yield传递给f,因此在第二次运行时才能触发,因此,send()一般前面会先运行一次生成器。若send()为首次运行生成器,则value值需为None,此时的send()功能与__next__()相同