生成器
生成器的实质就是迭代器.
生成器的特点和迭代器的特点一样,取值方式和迭代器一样( _next_() ).
生成器一般由生成器函数或者生成器表达式来创建.
其实就是手写的迭代器.
一.生成器函数:
1.生成器函数:
def func(): print("111") yield 222 g = func() print(g) 结果: <generator object func at 0x00000000024958E0> #生成器
由于函数中存在了 yield ,那么这个函数就是一个生成器函数,这个时候在执行这个函数的时候,就不在是函数的执行了,而是获得这个生成器.
def func(): print("111") yield 222 g = func() #这个时候函数不会执行,而是获得生成器. ret = g.__next__() #这个时候函数才会执行,yield的作业和return一样,也是返回数据. print(ret) 结果: 111 222
所以,我们可以执行 _next_ 来执行生成器.
2.yield 与 return 的区别
yield 是分段执行一个函数, 但是return 是直接停止执行函数.
def func(): print("111") yield 222 print("333") yield 444 gener = func() ret = gener.__next__() print(ret) ret1 = gener.__next__() print(ret1) ret2 = gener.__next__() #最后一个 yield 执行完毕,再次 _next_() 会报错,也就是说和return无关了. print(ret2) 结果 Traceback (most recent call last): 111 File "E:/wanchengdezuoye/复习/复习.py", line 1052, in <module> 222 ret2 = gener.__next__() 333 StopIteration 444
所以说,当程序运行完最后一个yield ,那么后面继续进行 _next_(),程序会报错.
3.生成器的作用:
比如: 你比较喜欢吃苹果,特别喜欢吃,那么如果你一次性的将你这一年的苹果都买了,那么问题来了,你的苹果放在那里,占用内存.
def func(): li = [] for i in range(1,500): li.append("买了"+str(i)+"苹果") return li ret = func() print(ret) 结果: ['买了1苹果', '买了2苹果', '买了3苹果', '买了4苹果', '买了5苹果',
'买了6苹果',.............,'买了499苹果']
但是如果你想吃的时候,想吃一个我去买一个,想吃就买,这样就可以既省内存,还可以保鲜.
def func(): for i in range(1,500): yield "买了"+str(i)+"苹果" ret = func() print(ret.__next__()) print(ret.__next__()) print(ret.__next__()) print(ret.__next__()) 结果: 买了1苹果 买了2苹果 买了3苹果 买了4苹果
总结:第一种是直接一次性全部拿出来,会很占用内存.
第二种使用生成器,一次就一个,用多少拿多少,节省内存.
4.生成器的 send() 方法:
send() 和 _next_() 的区别:
①.一样都可以让生成器执行下一个yield.
②.send() 可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器代码的时候不能使用 send() [因为send不能给最后一个yield发送值
def func(): print("我喜欢吃什么?") #执行的ret1 a = yield "包子" print(a) b = yield "馒头" #a = 金玉满堂,馒头,执行ret2 print(b) c = yield "韭菜盒子" #b = 乱炖,韭菜盒子,执行ret3 print(c) yield "OVER" #c = 煎饼.执行ret4 send不能给最后一个yield发送值. gen = func() ret1 = gen.__next__() print(ret1) ret2 = gen.send("金玉满堂") print(ret2) ret3 = gen.send("乱炖") print(ret3) ret4 = gen.send("煎饼") print(ret4) 结果: 我喜欢吃什么? 包子 金玉满堂 馒头 乱炖 韭菜盒子 煎饼 OVER
二.推导式
1.列表推导式:
例如:给出一个列表,通过循环,向列表中添加1-13
li = [] for i in range(1,14): li.append(i) print(li) 结果: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
用列表推导式:
li = [i for i in range(1,14)] print(li) 结果: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
列表推导式语法: [结果 for循环 判断]
例如:从1-25的树中找出能被2整数的数,添加到列表中. li = [i for i in range(1,26) if i % 2 == 0] print(li) 结果: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]
例如2;找出names中带有两个e的人
names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,'Joe'],
[ 'Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry' , 1]]
lst = [name for line in names for i in line if type(name) == str and name.count("e") == 2]
print(lst)
结果:
['Jefferson', 'Wesley', 'Steven', 'Jennifer']
2.字典推导式:
语法: {k:v for循环 条件筛选}
例如:
例1 :将[11,22,33,44] =>变成 {0:11,1:22,2:33,3:44} 算法: dic = {} li = [11,22,33,44] for i in range(len(li)): dic[i] = li[i] print(dic) 字典推导式: li = [11,22,33,44] dic = {i:li[i] for i in range(len(li))} print(dic) 结果: {0: 11, 1: 22, 2: 33, 3: 44}
例2 :将dic = {"jj": "林俊杰", "jay": "周杰伦", "zs": "赵四", "ln":"刘能"}中的键和值对调.
dic = {"jj": "林俊杰", "jay": "周杰伦", "zs": "赵四", "ln":"刘能"}
dic2 = {v:k for k,v in dic.items()}
print(dic2)
结果:
{'林俊杰': 'jj', '周杰伦': 'jay', '赵四': 'zs', '刘能': 'ln'}
3.集合推导式:
语法: {结果 for循环 条件筛选}
li = [1,2,5,4,6,4,6,8,9,9,] set = {i for i in li} print(set) 结果: {1, 2, 4, 5, 6, 8, 9}
三.生成器表达式:
语法: (结果 for循环 条件筛选) (注意:元组是没有推导式的!!!!!!!!)
生成器表达式可以直接获取生成器对象,生成器对象可以直接进行for循环.
用生成器表达式获取1-100内能被3整除的数 s = (i for i in range(1,100) if i % 3 == 0) for el in s: print(el) 结果: 3 6 9 12 ....... 99
1. 生成器表达式 与 列表推导式 的区别:
①.列表推导式比较耗内存,一次性加载.生成器表达式几乎不占用内存,使用的时候才分配和使用内存.
②.得到的值不一样,列表推导式得到的是一个列表,生成器表达式获取的是一个生成器.
比如: 同样一筐鸡蛋,列表推导式:直接拿一筐鸡蛋,--生成器表达式:拿到一只老母鸡,需要鸡蛋的时候就下个鸡蛋.
2.生成器的懒惰机制:生成器只有在访问的时候才取值.
def func(): print(111) yield 222 yield 333 g = func() #生成器 g g1 = (i for i in g) #生成器
#g3 = func()
#g2 = (i for i in g3) #这样,g2会有结果,因为g3获取数据的时候,func()被执行. g2 = (i for i in g1) #生成器 print(list(g)) #[222,333]源头,获取g的数据,这是func()被执行,打印[222,333],从源头将数据拿走 print(list(g1)) #[]执行到g1的时候,源头的数据已经被 g 拿走了,源头没有数据了 print(list(g2)) #[]这里也没有值了. 结果: 111 [222, 333] [] []
********面试题*******(有坑)
def add(a,b): #求和 return a + b def test(): #生成器函数 # for r_i in range(4): #0-3 0,1,2,3 yield r_i g = test() #获取生成器 # n = 1 # n = 10 # n = 5 #g = 0,1,2,3 = i = 0,1,2,3 # (add(n,i) for i in (add(n,i) for i in (add(n,i) for i in g))) #n=5 ******** #15,16,17,18 #10,11,12,13 # 5,6,7,8 for n in [1,10,5]: g = (add(n,i) for i in g) print(list(g)) #到最后往里面放数据****** 结果: [15,16,17,18]