DAY12 生成器初始与列表生成式
初识生成器
通过前面学习的迭代器,我们知道,迭代器十分节省内存。如果在某些情况下,我们也需要节省内存,就只能自己写,我们自己用python代码实现的迭代器就称为生成器。
生成器:生成器就是自己用python代码写的迭代器,生成器的本质就是迭代器。
(1)因为本质是迭代器,所以自带了__iter__方法和__next__方法。
(2)特点:惰性运算,开发者自定义。
python中提供的两种方法构建生成器:
(1)生成器函数:与普通函数的return不同,而是使用yield关键字。yield语句一次返回一个结果,在每个结果中间挂起函数,以便下次从它离开的地方再次运行。
(2)生成式推导式:类似于列表推导式,但是不是一次性构建一个列表,而是按需每次返回一个值。
构建生成器方式一:生成器函数
一个包含yield关键字的函数就称为生成器函数。与普通函数的return不同,函数一遇到return就会结束函数并给函数的执行者返回值;但是yield不会结束函数,而是返回值给'生成器对象.__next__()',每次获得可迭代对象,就会推动函数的进行。
#一。回顾普通函数与return def func1(x): x +=1 return x #函数遇到return关键字,就会结束函数并返回值给函数的执行者。 res = func1(6) #函数的执行命令,并且接受函数的返回值 print(res) #二。生成器函数,yield关键字。 def gen1(x): x +=1 print(1111) yield x #带有yield关键字的函数,称为生成器函数。 print(2222) yield x gen_obj = gen1(5) #此时这个就不是函数的执行者了,是生成器的对象 print(gen_obj) >>><generator object gen1 at 0x000001DFD052FE08> print(gen_obj.__next__()) >>> #一个yield关键字,会把值返回个一个next,并且把函数挂起。 1111 6 print(gen_obj.__next__()) #第二个生成器对象,会从挂起处继续向下执行,遇到第二个yield返回。 >>> 2222 6 print(gen_obj.__next__()) #生成器本质就是迭代器,只能一路走到黑,不能回头,所以报错。 >>> StopIteration #生成器范例二: def func1(x): x +=1 yield x x +=3 yield x x +=5 yield x g1 = func1(5) #g1为生成器对象 print(g1.__next__()) #一个yield对应一个next,yield把值返回,所以x为6 print(g1.__next__()) #一个yield对应一个next,yield把值返回,所以6+3=9 print(g1.__next__()) #一个yield对应一个next,yield把值返回,所以9+5=14 '''因为生成器本质就是迭代器,所以符合迭代协议''' for i in g1: print(i) >>> 6 9 14
总结1:yield与return的区别?
答:return结束函数,给函数的执行者返回值。yield不会结束函数,一个next调用对应一个yield,yield把值返回给"生成器对象.__next__()"
总结2:生成器与迭代器的差别?
答:从内存级别来看,迭代器需要可迭代对象来进行转化,可迭代对象非常占内存;生成器直接创建,不需要转换,从本质上就节省内存。
send与next
send : send与next在取值上是一样的,执行一个yield方法。但是send可以给上一个yield传递值。
谨记:(1)第一次取值永远都是next。
(2)最后一个yield永远也得不到send传的值。
#一。send与next在取值上是一样的,都是对应一个yield关键字。 def func1(): print(1) yield 6 print(2) yield 7 print(3) yield 8 g1 = func1() print(next(g1)) print(g1.send('alex')) print(next(g1)) >>> 1 6 2 7 3 8 #二。但是send可以给上一个yield传递值 def func1(): count = yield 6 print(count) yield 7 yield 8 g1 = func1() print(next(g1)) #遇到next调用一个yield,此时yield返回值为6,所以next(g1)为6,yield无实体值, 所以count为None g1.send('alex') #遇到send取值与next一致,对一个yield,但是会给上一个yield赋值,所以 count=‘alex print(next(g1)) #同上。 >>> 6 alex 8
构建生成器方式二:生成器推导式
前提知识:列表生成式/列表推导式
1.一行代码几乎搞掂需求简单的任何列表,方便。
2.不易排错,不建议超过三次循环。
#一。循环模式 [ 变量 for 变量 in iterable ] list = [x for x in range(101)] print(list) >>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100] l2 = [i**2 for i in range(1,11)] print(l2) >>> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] #二。筛选模式 [ 加工后的变量 for 变量 in Iterable if 条件] '''30以内被3整除的数''' print([ i for i in range(1,30) if i%3==0]) >>> [3, 6, 9, 12, 15, 18, 21, 24, 27] '''30以内被3整除的数的平方''' print([ i**2 for i in range(1,30) if i%3==0]) >>> [9, 36, 81, 144, 225, 324, 441, 576, 729] '''找出嵌套列表中名字含有两个'e'的所有名字''' names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] print([j for i in names for j in i if j.count('e') >= 2]) >>> ['Jefferson', 'Wesley', 'Steven', 'Jennifer']
生成器推导式
1.十分简单,只需要把列表推导式[]换成()即可
# 通过生成式推导式构建生成器generator g1 = (i for i in range(1,10000)) print(g1) print(g1.__next__()) print(g1.__next__()) print(g1.__next__()) print(g1.__next__()) >>> <generator object <genexpr> at 0x00000253C09DFE08> 1 2 3 4