Python的生成器和生成器表达式

一,生成器和生成器表达式

什么是生成器,生成器实质就是迭代器,在python中有三种方式来获取生成器:

1. 通过生成器函数

和普通函数没有区别,里面有yield的函数就是生成器函数,生成器函数在执行的时候.,默认不会执行函数体.,返回生成器通过生成器的__next__()分段执行这个函数
send() 给上一个yield传值, 不能再开头(没有上一个yield), 最后一个yield也不可以用send()

先来看一下简单的函数

1 def xue():    #创建一个函数
2     print('')
3     return 'Python'    #生成器
4 xue1 = xue()    
5 print(xue1)
6 结果:
7 8 Python

函数中的return换成yield这就是一个生成器

1 def xue():
2     print('')
3     yield 'Python'
4 xue1 = xue()
5 print(xue1)
6 结果:
7 <generator object xue at 0x000002B7F1115660>

运行结果跟上面的不一样,因为由于函数中存在yield,那么这个函数就是一个生成器函数,这个时候我们在执行这个函数的时候,就不再是函数的执行了,而是获取这个生成器,如何使用呢?想想迭代器,生成器的本质就是迭代器,所以,我们可以直接执行__next__()来执行以下生成器

1 def xue():
2     print('')
3     yield 'Python'
4 xue1 = xue()    #这时候函数不会执行,而是获取到生成器
5 YJ = xue1.__next__()#这个时候函数才会执⾏行yield的作⽤用和return一样. 也是返回数据
6 print(YJ)
7 结果:
8 9 Python

yield和return的区别就是,yield是分段来执行一个函数,return是直接停止执行函数

 1 def xue():
 2     print('')
 3     yield 'Python'
 4     print('也学')
 5     yield 'java'
 6 xue1 = xue()    #这时候函数不会执行,而是获取到生成器
 7 YJ = xue1.__next__()
 8 print(YJ)
 9 YJ1 = xue1.__next__()
10 print(YJ1)
11 YJ2 = xue1.__next__()    #最后一个yield执行完毕,再次__next__()程序报错,也就是说和return无关了
12 print(YJ2)

结果:

 当程序运行完最后一个yield,那么后面继续进行__next__()

需求:某学校要订购50000套校服,工厂直接生产50000套校服,但是学校也没有5000个学生,也没有教室放置一次性生产那么多,最后的效果会是什么呢?直接生产50000件校服,刷一下量大的话把你内存撑满!!!

1 def xiaofu():
2     lst = []
3     for el in range(0,50000):
4         lst.append('校服'+str(el))
5     return lst
6 xiaofu()
7 print(xiaofu())

我要一套你给一套,一共50000,是不是很完美

def xiaofu():
    for el in range(0,50000):
        yield '校服'+str(i)
xf = xiaofu()
print(xf.__next__())    
print(xf.__next__())
print(xf.__next__())
print(xf.__next__())

两者的区别,第一种是直接一次性全部拿出来,会很占用内存,第二种是使用生成器,一次就一个,用多少生产多少,直到生产完,生成器是一个一个的指向下一个,不会回去,__next__()到哪,指针就指到哪儿,下一次继续获取指针指向的值

send方法,send和__next__()一样都可以让生成器执行到下一个yield

 1 def xue():
 2     print('要学')
 3     a = yield 'Python'
 4     print('a=',a)
 5     b = yield 'java'
 6     print('b=', b)
 7     c = yield 'PHP'
 8     print('c=', c)
 9     yield 'YJ加油!'
10 xue1 = xue()    #获取生成器
11 fh = xue1.__next__()
12 print(fh)
13 #结果:要学
14 # Python
15 fh1 = xue1.send('VB')
16 print(fh1)
17 # 结果:a= VB
18 # java
19 fh2 = xue1.send('C#')
20 print(fh2)
21 # 结果:b= C#
22 # PHP
23 fh3 = xue1.send('C++')
24 print(fh3)
25 # 结果:c= C++
26 # YJ加油!

send和__next__()的区别

1.send和next()都是让生成器向下走一次

2.send可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器代码的时候不能使用send()

生成器可以使用for需变换来循环获取内部元素:

 1 def xue():
 2     print('要学')
 3     a = yield 'Python'
 4     print('a=',a)
 5     b = yield 'java'
 6     print('b=', b)
 7     c = yield 'PHP'
 8     print('c=', c)
 9     yield 'YJ加油!'
10 xue1 = xue()
11 for el in xue1:
12     print(el)

结果:

 2. 通过各种推导式来实现生成器

1. 列表推导式 [结果 for循环 条件筛选]
2. 字典推导式 {k:v for循环 条件筛选}
3. 集合推导式 {k for循环 条件}

3. 通过数据的转换也可以获取生成器

二.,生成器表达式,列表推导式,以及其他推导式 

我们来看一段代码,给出一个空列表,通过for循环向列表添加1-15个数字:

1 lst = []
2 for i in ramge(1,16):
3     lst.append(i)
4 print(lst)
5 结果:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,15]

列表推导式写法:

 

1 #推导式
2 lst = [i for i in range(1,16)]
3 print(lst)
4 结果:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

 

两者的结果是不是一样?列表推导式比较简洁,列表推导式是通过一行来构建你要的列表,列表推导式看起来代码简单,但是出现错误之后就很难排查

列表推导式的写法:[结果  for  变量  in  可迭代对象]

需求:从1-19岁写入到lst列表中

1 lst = [str(i)+'' for i in range(1,20)]
2 print(lst)
3 结果:['1岁', '2岁', '3岁', '4岁', '5岁', '6岁', '7岁', '8岁', '9岁', '10岁', '11岁', '12岁', '13岁', '14岁', '15岁', '16岁', '17岁', '18岁', '19岁']

我们还可以对列表中的数据进行筛选,筛选写法:

[ 结果  for  变量  in  可迭代对象  if  条件 ]

#获取1-100内所有的偶数

1 lst = [i for i in range(1,100) if i % 2 ==0]

2 print(lst) 

生成器表达式和列表推导式的语法基本是一样的,只是把[]换成()

1 YJ = (i for i in range(1,15))
2 print(YJ)
3 结果为:
4 <generator object <genexpr> at 0x000002CE3ABA5660>

上面的结果就是一个生成器,我们可以使用for循环来获取这个生成器

 1 YJ = ('我今年%s岁了' % i for i in range(20))
 2 for i in YJ:
 3     print(i)
 4 结果:
 5 我今年0岁了
 6 我今年1岁了
 7 我今年2岁了
 8 我今年3岁了
 9 我今年4岁了
10 我今年5岁了
11 我今年6岁了
12 我今年7岁了
13 我今年8岁了
14 我今年9岁了
15 我今年10岁了
16 我今年11岁了
17 我今年12岁了
18 我今年13岁了
19 我今年14岁了
20 我今年15岁了
21 我今年16岁了
22 我今年17岁了
23 我今年18岁了
24 我今年19岁了

生成器表达式也是可以进行筛选的

获取1-100内能被3整除的数

1 # 获取1-100内能被3整除的数
2 YJ = (i for i in range(1,100) if i % 3 == 0)
3 print(YJ)
4 #结果:<generator object <genexpr> at 0x000002164AC95660>
5 for i in YJ:    #使用for循环来循环这个生成器拿结果
6     print(i)
7 结果;
8 3,6,9,12,15...99
100以内能被3整除的数的平方
1 YJ = (i * i for i in range(100) if i % 3 == 0)
2 for i in YJ:    #使用for循环来循环这个生成器拿结果
3     print(i)

生成器表达式跟列表推导式的区别:

1.列表推导式比较耗内存,一次性加载,生成器表达式几乎不占内存,使用的时候才分配和使用内存

2.得到的值不一样,列表推导式得到的是一个列表,生成器表达式获取的是一个生成器

生成器有惰性机制,生成器只有访问的时候才取值,说白了,你找它要它才会给你值,不找不给它也不会执行

字典推导式:

根据名字也能猜到,推到出来的是字典

1 dic = {'a' : 1, 'b' : 2, 'c' : 3}
2 dic1 = {dic[key]  : key for key in dic}
3 print(dic1)
4 #结果:{1: 'a', 2: 'b', 3: 'c'}

集合推导式:

集合推导式可以帮我们直接生成一个集合,集合的特点是无序,不重复,所有集合推导式自带去重功能

1 lst = [1,1,2,3,3,66,66,8,9]
2 a = {abs(i) for i in lst}
3 print(a)
4 #结果:{1, 2, 3, 66, 8, 9}

总结:推导式有,列表推导式,字典推导式,集合推导式,切记切记!!!元组是没有推导式的

生成器表达式:(结果 for 变量量 in 可迭代对象 if 条件筛选)

生成器表达式可以直接获取到生成器对象,生成器对象可以直接进行for循环,生成器具有惰性机制!!!

2019年11月13日

posted @ 2019-11-13 15:22  YJ同心  阅读(...)  评论(... 编辑 收藏