生成器函数及表达式,列表推导式
生成器
生成器的本质就是迭代器
生成器一般由生成器函数或者生成器表达式来创建
其实就是手写的迭代器
def func(): print("娃哈哈") yield 1 # return和yield都可以返回数据 print("呵呵呵") gen = func() # 获取生成器,不会执行你的函数. 拿到的是一个生成器 print(gen)# >>> <generator object func at 0x0000021441B634F8> #不会拿到值,拿到的是个内存地址
生成器的特点
和迭代器一样.取值方式和迭代器一样(__next__()
send(): 给上一个yield传值).
def func(): print("娃哈哈") yield 1 print("呵呵呵") gen = func() # 不会执行你的函数. 拿到的是生成器 print(gen.__next__()) #执行函数. 执行到下一个yield. >>> 结果: 娃哈哈 1
生成器函数
和普通函数没有区别. 里面有yield的函数就是生成器函数.
生成器函数在执行的时候. 默认不会执行函数体. 返回生成器
def func(): print("娃哈哈") yield 1 # return和yield都可以返回数据 print("呵呵呵") gen = func() # 不会执行你的函数. 拿到的是生成器 print(gen) >>> <generator object func at 0x00000181098B34F8>
yield: 相当于return 可以返回数据. 但是yield不会彻底中断函数. 分段执行函数
yield好处
''' 生成器有什么好处呢?就是不会一下子在内存中生成太多数据 假如我想让工厂给学生做校服,生产2000000件衣服,我和工厂一说,工厂应该是先答应下来,然后再去生产,我可以一件一件的要,也可以根据学生一批一批的找工厂拿。 而不能是一说要生产2000000件衣服,工厂就先去做生产2000000件衣服,等回来做好了,学生都毕业了。。。 ''' def order(): lst = [] for i in range(200000): lst.append("衣服"+str(i)) return lst g = order() print(g) # 这个数据太庞大了,一下处理2000000件衣服... #我们加了yield就不一样了,我们可以根据自己的需求要衣服 def order(): """生产衣服""" for i in range(2000000): yield "生产了第%s件衣服"%i g =order() print(g.__next__()) #要一件衣服 print(g.__next__()) #再要一件衣服 print(g.__next__()) #再要一件衣服 num = 0 for i in g: #要一批衣服,比如5件 print(i) num +=1 if num == 5: break
通过生成器的__next__()分段执行这个函数.
send() 给上一个yield传值, 不能再开头(没有上一个yield), 最后一个yield也不可以用send()
#send() 和__next__()是一样的. 可以执行到下一个yield, 可以给上一个yield位置传值 def func(): print("我是第一个段") a = yield 123 print(a) print("周杰伦是第二段") b = yield 456 print(b) # ?? print("林俊杰是第三段") c = yield 789 print(c) print("王力宏是最后一个段") yield 79 # 最后收尾一定是yield g = func() print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__()) >>> 我是第一个段 123 None 周杰伦是第二段 456 None 林俊杰是第三段 789 None 王力宏是最后一个段 79 g = func() #获取生成器 print(g.__next__()) # 没有上一个yield 所以不能使用send() 开头必须是__next__() print(g.send("煎饼果子")) #向第一个yield传值 print(g.send("韭菜盒子")) #向上一个yield传值 print(g.send("锅包肉")) #向上一个yield传值 >>> 我是第一个段 123 煎饼果子 周杰伦是第二段 456 韭菜盒子 林俊杰是第三段 789 锅包肉 王力宏是最后一个段 79
推导式
用一句话来生成一个列表
列表推导式
[结果 for循环 条件筛选]
#50以内的奇数 lst = [i for i in range(50) if i%2==1] print(lst) >>> [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49]
字典推导式 {k:v for循环 条件筛选}
#key和value颠倒 dic = {"jj": "林俊杰", "jay": "周杰伦", "zs": "赵四", "ln":"刘能"} d = {v : k for k,v in dic.items()} print(d) >>> {'林俊杰': 'jj', '周杰伦': 'jay', '赵四': 'zs', '刘能': 'ln'}
集合推导式
{k for循环 条件}
#集合有去重作用 lst = [1, 1, 4, 6,7,4,2,2] s = { el for el in lst } print(s) >>> {1, 2, 4, 6, 7}
生成器表达式
元组tuple没有推导式,生产器表达式
(结果 for循环 条件)
tu = (i for i in range(10)) # 没有元组推导式. 生成器表达式 print(tu) # 生成器 >>> <generator object <genexpr> at 0x00000136D23B34F8> #既然是生成器那我们就可以用 __next__来拿值 print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) >>> 0 1 2 3 4 5 6
特点:
惰性机制
只能向前
节省内存
#值2000块的面试题!!! def func(): print(111) yield 222 yield 333 g = func() # 获取生成器 g1 = (i for i in g) # 生成器 g2 = (i for i in g1) # 生成器 print(list(g)) # 111 [222,333] 源头. 从源头把数据拿走了 # print(list(g1)) # [] 都是要从源头拿数据的,所以这里执行的时候. 源头已经被g取走了,没有了数据 # print(list(g2)) # [] 这里也没有值了
#求和 def add(a, b): return a + b # 生成器函数 # 0-3 def test(): for r_i in range(4): yield r_i
升级版!!!
#0,1,2,3 g = test() # 获取生成器 for n in [2, 10]: g = (add(n, i) for i in g) # 当n = 2时 g不取值,因没有值,只是单纯遍历一下并没有执 #行,这时候 g = (add(n,),for i in g) # 当n = 10时 g = (add(n+i),for i in add(n+i),for i in g) # 因惰性机制,运行到最后 g开始取值,从源头取值,源头g = 0,1,2,3 print(list(g)) # 代数,n =10时 g =(add(10+i),for i in add(10+0,1,2,3)) # i = 10,11,12,13 # 20,21,22,23 #list(g) 一次性全拿出来,所以 list(g) 结果 20,21,22,23