生成器和生成器函数,推倒式
一. 生成器和生成器函数
1. 生成器.
生成器实质就是迭代器. 在python中有三种方式来获取生成器: 1. 通过生成器函数 2. 通过各种推导式来实现生成器 3. 通过数据的转换也可以获取生成器
def f1(): print(\'hello,world\') return \'good\' def f2(): print(\'hello,world\') yield \'good\' #把return换成yield就是生成器 print(f1()) #hello,world good print(f2()) #<generator object f2 at 0x0000027BD3169390>
运行的结果和上面不一样, 是因为函数中存在了yield. 那么这个函数就是一个生成器函数. 这个时候再执行这个函数的时候, 就不再是函数的执行了. 而是获取这个生成器,生成器的本质是迭代器. 所以可以直接执行__next__()来执行生成器.
def f(): print(\'茉莉花\') yield \'歌曲\' print(\'走出非洲\') yield \'电影\' print(\'雷雨\') yield \'话剧\' print(\'妈妈咪呀\') yield \'音乐剧\' g = f() #函数不会执行,只会获得函数的生成器 print(g.__next__()) #函数执行到第一个yield,和return一样,yield也有返回值,但不会结束函数. # 茉莉花 # 歌曲 print(g.__next__()) #继续执行下一个yield # 走出非洲 # 电影 print(g.__next__()) # 雷雨 # 话剧 print(g.__next__()) # 妈妈咪呀 # 音乐剧
print(g.__next__()) #最后一个yield执行完毕,继续__next__,会报错:StopIteration
send和__next__()一样都可以让生成器执行到下一个yield, send还能给上一个yield赋值,所以第一个不能用send,最后一个也不要传值.
def f(): print(\'茉莉花\') a = yield \'歌曲\' #把yield赋值给a print(\'走出非洲\') b = yield \'电影\' print(\'雷雨\') c = yield \'话剧\' print(\'妈妈咪呀\') d = yield \'音乐剧\' g = f() #获取生成器 print(g.__next__()) #第一个必须要用__next__,不能用send() print(g.send(1)) #从左边的a开始执行,到下一个yield结束.把1的值传给a print(g.send(2)) #从左边的b开始执行,到下一个yield结束.把2的值传给b print(g.send(3)) #从左边的c开始执行,到下一个yield结束.把3的值传给c # print(g.send(4)) #从左边的d开始执行,把4的值传给d,无法找到下一个yield.结果会报错.所以最后不要用send()传值
生成器可以使用for循环来循环获取内部的元素:
def f(): print(\'茉莉花\') yield \'歌曲\' print(\'走出非洲\') yield \'电影\' print(\'雷雨\') yield \'话剧\' print(\'妈妈咪呀\') yield \'音乐剧\' g = f() for el in g: print(el) # 茉莉花 # 歌曲 # 走出非洲 # 电影 # 雷雨 # 话剧 # 妈妈咪呀 # 音乐剧
二. 推倒式
1. 列表推导式
语法: lst = [最终结果 for 变量 in 可迭代对象 if 条件]
# 1. 获取1-50内能被3整除的数 lst1 = [i for i in range(1,51) if i % 3 == 0] print(lst1) #[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48] # 2. 50以内能被3整除的数的平方 lst2 = [i*i for i in range(1,51) if i % 3 == 0] print(lst2) #[9, 36, 81, 144, 225, 324, 441, 576, 729, 900, 1089, 1296, 1521, 1764, 2025, 2304] # 3. 寻找名字中带有两个e的⼈的名字 names = [[\'Tom\', \'Billy\', \'Jefferson\' , \'Andrew\' , \'Wesley\' , \'Steven\' , \'Joe\'],[\'Alice\', \'Jill\' , \'Ana\', \'Wendy\', \'Jennifer\', \'Sherry\' , \'Eva\']] lst3 = [name for first in names for name in first if name.count("e") == 2] print(lst3) #[\'Jefferson\', \'Wesley\', \'Steven\', \'Jennifer\']
2. 生成器表达式
语法: gen = (最终结果 for 变量 in 可迭代对象 if 条件)
#10以内的偶数 gen = (i for i in range(11) if i%2==0) print(gen) #<generator object <genexpr> at 0x000001BC2BB29390> print(gen.__next__()) #0 print(gen.__next__()) #2 print(gen.__next__()) #4 print(gen.__next__()) #6 print(gen.__next__()) #8 print(gen.__next__()) #10
生成器表达式和列表推导式的区别:
(1). 列表推导式比较耗内存. 一次性加载. 生成器表达式几乎不占用内存. 使用的时候才分配和使用内存
(2). 得到的值不一样. 列表推导式得到的是一个列表. 生成器表达式获取的是一个生成器.
# 生成器的惰性机制 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,但g2的数据来源是g1 print(list(g)) #获取g中的数据. 这时func()才会被执行. 打印111.获取到222. g完毕 # 111 # [222] print(list(g1)) #获取g1中的数据. g1的数据来源是g. 但是g已经取完了. g1也就没有数据了 #[] print(list(g2)) #获取g2中的数据. g2的数据来源是g1. 但是g1没有数据. g2也就没有数据. #[]
注意:深坑==> 生成器. 要值得时候才拿值.
3. 字典推倒式
#把lst1的值作为key,与之相对应的lst2的值作为value lst1 = ["jack", "tom", "jason", "steve"] lst2 = [\'singer\', "dancer", "cook", "writer"] dic = { lst1[i]:lst2[i] for i in range(len(lst1))} print(dic) #{\'jack\': \'singer\', \'tom\': \'dancer\', \'jason\': \'cook\', \'steve\': \'writer\'}
4. 集合推倒式
集合推导式可以帮我们直接⽣生成一个集合. 集合的特点: ⽆无序, 不重复. 所以集合推导式自带去重功能
lst = [1,2,1,3,4,5,6,3,3,12,10,5] s = {i for i in lst} print(s) #{1, 2, 3, 4, 5, 6, 10, 12}
总结:
推导式有, 列表推导式, 字典推导式, 集合推导式, 没有元组推导式
生成器表达式: (结果 for 变量量 in 可迭代对象 if 条件筛选)
生成器表达式可以直接获取到生成器对象. 生成器对象可以直接进行for循环. 生成器具有惰性机制.