Python生成器/推导式/生成器表达式
一 生成器
生成器的本质就是迭代器
生成器的特点和迭代器一样,取值方式和迭代器一样(__next__(), send(): 给上一个yield传值)
生成器一般由生成器函数或者生成器表达式来创建
其实就是手写的迭代器
1 def func(): 2 print("111") 3 yield 123 4 5 ret = func() 6 print(ret)
由于函数中含有yelid,那么这个函数就是生成器函数, 且执行这个函数的时候就不再试函数的执行了,而是获取这个生成器.
如何使用:
def func(): print("111") yield 123 ret = func() #这个时候函数不会被执行而是获取生成器 print(ret) #按以前函数的方法执行会打印出地址 s = ret.__next__() print(s) 结果 <generator object func at 0x000002EB35432C50> 111 123
yield 是分段执行这个函数, return 是直接停止执行这个函数
1 def func(): 2 print(123) 3 yield 456 4 print(789) 5 yield 147 6 print(258) 7 yield 369 8 9 ret = func() 10 print(ret.__next__()) 11 print(ret.__next__()) 12 print(ret.__next__()) 13 print(ret.__next__()) #最后一个yield 执行完毕,再次 __next__()程序会报错, 与return无关 14 15 16 #结果 17 123 18 456 19 789 20 147 21 258 22 369 23 Traceback (most recent call last): 24 File "E:/Python/day13/练习.py", line 48, in <module> 25 print(ret.__next__()) 26 StopIteration # 报错
生成器函数:
1 和普通函数没有区别,里面有 yield 的函数就是生成器函数
2 生成器函数在执行的时候, 默认不会执行函数体, 返回生成器
3 通过生成器的__next__() 分段执行这个函数
4 send() 给上一个 yield 传值, 不能再开头(没有上一个yield) 最后一个yield 也不能用send()
生成器作用:
举一个例子---
比如 你比较喜欢吃鸡蛋,一次性给你买几千个鸡蛋 , 够你吃几年了, 那么这些鸡蛋你怎么存放, 需要很多地方存放,
所以这样你想吃鸡蛋了就去买一个,想吃了就买一个,是不是也能一直吃鸡蛋
ef func(): for i in range(1,5000): yield "鸡蛋"+str(i) ret = func() print(ret.__next__()) print(ret.__next__()) print(ret.__next__()) print(ret.__next__()) print(ret.__next__()) # 结果 鸡蛋1 鸡蛋2 鸡蛋3 鸡蛋4 鸡蛋5
区别 : 如果直接买几千个给你吃 , 会很占地方, 在程序中就是很占内存, 然而上面的方法是 你想吃就买一个, 不会占地方
所以 生成器非常省内存
send()
方法: send()方法和__next__方法一样都可以让生成器执行到下一个yield
def func(): print(1) a = yield 2 print(a) b = yield 4 print(b) c = yield 6 print(c) d = yield 8 gen = func() ret = gen.__next__() print(ret) ret1 = gen.send("金") print(ret1) ret2 = gen.send("天") print(ret2) ret3 = gen.send("下") print(ret3) #结果 1 2 金 4 天 6 下 8
send() 和 __next__的区别:
1 send 和next() 都是让生成器向下走一次
2 send 可以个上一个yield的位置传递值,不能给最后一个 yield 发送值,在第一次执行生成器代码的时候不能使用 send()
推导式:
列表推导式: 就是用一句话来生成一个列表
语法: [结果 for循环 判断]
lis = [i for i in range(50) if i%2==1] print(lis) 结果 [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循环 条件判断}
lis = [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] dic = {i:lis[i] for i in range(len(lis)) if i%3!=0} print(dic) #结果 {1: 3, 2: 5, 4: 9, 5: 11, 7: 15, 8: 17, 10: 21, 11: 23, 13: 27, 14: 29, 16: 33, 17: 35, 19: 39, 20: 41, 22: 45, 23: 47}
集合推导式
语法: {k for循环 条件判断}
lis = [1,2,3,4,5,6,7,8,12,1,2,3,4,3,3,33,44,55,11,22,33,44,55,333,442,223,3,21] set = {i for i in lis} print(set) #结果 {1, 2, 3, 4, 5, 6, 7, 8, 33, 11, 12, 44, 333, 21, 22, 55, 442, 223}
生成器表达式:
格式: (结果 for循环 条件判断)
特点:
1 惰性机制
2 只能向前
3 节省内存
重点: 面试题
def add(a,b): return a + b def test(): for te in range(4): yield te g = test() for i in [2,10]: g = (add(i,n) for n in g ) print(list(g))
上面的程序详解步骤: for i in [2,10]: g = (add(i,n) for n in g ) 由于for循环 i = 2的时候没有取值 所以当i = 10 的时候才取值 i = 2时 g = (add(i,n) for n in g ) 但是没有取值 i = 10时 g = (add(i,n) for n in (add(i,n) for n in g ) ) 把i 换成10 g = (add(10,n) for n in (add(10,n) for n in g ) ) 因为g 是0, 1 , 2 , 3 所以 10 11 12 13 g = (add(10,n) for n in (add(10,n) for n in g ) ) g = (add(10,n) for n in (10,11,12,13) ) g = [20,21,22,23]