python记录_day12 生成器
什么是生成器?
生成器的实质就是迭代器,我们能够从生成器中一个一的拿值
python中获取生成器的方式有三种:
1、通过生成器函数
2、通过生成器表达式
3、通过数据转换也可以获取生成器(某些对象执行一个方法就能返回一个生成器,这个现在用不到)
一、 生成器函数
1 def gen(): 2 代码块 3 yield 返回值 4 5 gen() #表示获取一个生成器,是一个内存地址,装的是代码,并不执行,用的时候才会执行
将return换成yield就是生成器函数了,上面就是生成器函数的格式。yield的作用是代码执行到这里就会停止,并且会返回一个值,所以代码能够分段执行,这就是生成器能逐个拿值的原因。
对于生成器函数,函数名+()并不表示函数执行,而是获取一个生成器对象。生成器装的是代码,只有在需要拿值的时候才会执行,这是生成器的惰性机制。
1、通过__next__访问生成器
1 # def gen(): 2 # print("娃哈哈") 3 # yield '爽歪歪' 4 # print("酸酸乳") 5 # yield 'AD钙奶' 6 # print('黄焖鸡米饭') 7 # 8 # ret = gen() #获取生成器对象,生成器装的是代码,要的时候运行一段 9 # print(ret) #<generator object gen at 0x0000000001E12F10> 生成器 generator 10 # print(ret.__next__()) #调用__next__才会执行,返回爽歪歪 11 # print(ret.__next__()) #调用__next__才会执行,返回Ad钙奶 12 #print(ret.__next__()) #StopIteration 迭代器,就找yield,找不到就会报错 往下运行,打印出了“黄焖鸡米饭”,但是找不到yield,所以报错
2、通过send()访问生成器
send()可以取值的同时给上一个yield位置传值
1 def func(): 2 print("水饺") 3 a = yield "大馅水饺" 4 print("a=", a) 5 print("烧饼") 6 b = yield "武大郎烧饼" 7 print("b=", b) 8 print("老婆饼") 9 c = yield "只要老婆不要饼" 10 print("c =",c) 11 12 g = func() 13 print("返回值是:",g.__next__()) 14 print("返回值是:",g.send("面条")) 15 print("返回值是:",g.send("面条")) 16 #print("返回值是:",g.send("面条")) #c = 面条 报错 StopIteration 17 18 #结果 19 水饺 20 返回值是: 大馅水饺 21 a= 面条 22 烧饼 23 返回值是: 武大郎烧饼 24 b= 面条 25 老婆饼 26 返回值是: 只要老婆不要饼
send和__next__的区别:
send()和__next__都可以让生成器向下走一段,但send可以给上一个yield位置传值,使用时不能在第一次,也不能给最后一个yield传值
二、推导式
1、列表推导式
语法:[ 结果 for循环 if判断 ]
1 lst = ["中岛美雪","夏川美里","原由子","汪峰","田震","那英","周杰伦"] 2 3 l = [name for name in lst if len(name) == 2 ] #这就是列表推导式 4 print(l)
2、字典推导式
语法:{ key:value for循环 if判断 }
dic ={"张无忌":"赵敏","杨过":"小龙女","郭靖":"黄蓉"} new_dic = {dic[k]: k for k in dic} #字典推导式 print(new_dic) new = {v:k for k,v in dic.items()} #字典推导式 print(new)
3、集合推导式
语法:{ 结果 for循环 if判断 }
lst = [1,2,3,1,2,4] se = {k for k in lst} #这是集合推导式 print(se) # {1,2,3,4}
集合推导式自带去重功能
5、没有元组推导式
6、生成器表达式
语法:( 结果 for循环 if判断 )
1 names = [['Tom',"Billy","Jefferson","Abdrew","Wesley","Steven","Joe"],['Alice',"Jill","Ana","Wendy",'Jennifer','Sherry','Eva']] 2 3 name_gen = (name for el in names for name in el if name.count("e")==2) 4 print(name_gen.__next__()) 5 print(name_gen.__next__()) 6 print(name_gen.__next__()) 7 8 9 结果 10 #Jefferson 11 #Wesley 12 #Steven
生成器拿值方式:
1、用__next__和send
2、使用for循环
1 gen = (i for i in range(1,100) if i % 3 == 0) 2 for num in gen: 3 print(num)
3、list(g),g是生成器
1 gen = (i * i for i in range(100) if i % 3 == 0) 2 print(list(gen))
结果是一个列表
[0, 9, 36, 81, 144, 225, 324, 441, 576, 729, 900, 1089, 1296, 1521, 1764, 2025, 2304, 2601, 2916, 3249, 3600, 3969, 4356, 4761, 5184, 5625, 6084, 6561, 7056, 7569, 8100, 8649, 9216, 9801]
生成器的特性:
1、节省内存
2、惰性机制 (不到最后使用时不会拿值)
3、拿值只能往前,不能后退
惰性机制+只能往前拿值特性的组合考察(深坑):
def func(): print(111) yield 222 g = func() # 生成器g g1 = (i for i in g) # 生成器g1. 但是g1的数据来源于g g2 = (i for i in g1) # 生成器g2. 来源g1 print(list(g)) # 获取g中的数据. 这时func()才会被执行. 打印111.获取到222. g完毕. print(list(g1)) # 获取g1中的数据. g1的数据来源是g. 但是g已经取完了. g1 也就没有数据 了 print(list(g2)) # 和g1同理 #结果 111 222 [] []
**小知识 yield from**
yield ffrom 可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回
def gen(): lst = ["花藤", "胡辣汤", "微星牌饼铛", "Mac牌锅铲"] yield from lst g = gen() #获取生成器 for el in g: print(el) 等价于: def gen(): lst = ["花藤", "胡辣汤", "微星牌饼铛", "Mac牌锅铲"] yield lst[0] yield lst[0] yield lst[0] yield lst[0] g = gen() #获取生成器 for el in g: print(el)
**面试题**
1、
def gen(): lst = ["花藤", "胡辣汤", "微星牌饼铛", "Mac牌锅铲"] lst2 = ["饼铛还是微星的好", "联想不能煮鸡蛋", "微星就可以", "还可以烙饼"] yield from lst #yield from 会迭代返回列表中的元素,执行完lst才会执行lst2,所以不会有交替打印的效果 yield from lst2 g = gen() for el in g: print(el) 效果: 花藤 胡辣汤 微星牌饼铛 Mac牌锅铲 饼铛还是微星的好 联想不能煮鸡蛋 微星就可以 还可以烙饼
2、
def add(a,b): return a + b def test(): for r_i in range(4): yield r_i g = test() for n in [2,10]: g = (add(n,i) for i in g) print(list(g)) #for循环第一次执行,n= 2时 g变为了 g = add(n,i) for i in (add(n,i) for i in test() 这时没取值,g也就不执行 # for第二次执行,n=10 此时 g= add(n,i) for i in (add(n,i) for i in test() 这时开始取值 ,g执行 结果: [20, 21, 22, 23]