一、生成器
生成器实质就是迭代器
两种方式写生成器:
1.生成器函数
2.生成器表达式
首先,我们先看一个很简单的函数:
def func(): print("111") return 222 ret = func() print(ret) 结果: 111 222
将函数中的return换成yield就是生成器
def func(): print("111") yield 222 gener = func() # 这个时候函数不会执行. 而是获取到⽣成器 ret = gener.__next__() # 这个时候函数才会执行. yield的作用和return一样. 也是返回 数据 print(ret) 结果: 111 222
yield和return的效果是一样的,yield是分段来执行一个函数,return是直接停止执行函数。
def func(): print("111") yield 222 print("333") yield 444 gener = func() ret = gener.__next__() print(ret) ret2 = gener.__next__() print(ret2) ret3 = gener.__next__() # 最后一个yield执行完毕. 再次__next__()程序报错, 也就是说. 和return无关了. print(ret3) 结果: 111 Traceback (most recent call last): 222 333 File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 55, in <module> 444 ret3 = gener.__next__() # 最后一个yield执行完毕. 再次__next__()程序报错, 也就是说. 和return无关了. StopIteration
生成器函数有什么用
普通的程序会占用内存
def buy(): lst = [] for i in range(10000): lst.append("衣服%s" % i) return lst lst = buy() print(lst)
利用生成器,需要一件拿一件节省内存空间
def cloth(): for i in range(10000): yield "衣服"+str(i) cl = cloth() print(cl.__next__) print(cl.__next__) print(cl.__next__) print(cl.__next__)
区别:第一种是直接一次性全部拿出来,会和占用内存,第二种使用生成器,一次就一个,用多少拿多少,生成器是一个一个的指向下一个,不会回去,__next__()到哪,指针就知道哪儿,下一次继续获取指针指向的值。
特性:和迭代器一样
1.节省内存
2.惰性机制
3.只能往前
生成器还可以使用__next__(), send()来访问生成器
send()可以给上一个yield位置传值
def eat(): print("我吃什么啊") a = yield "馒头" print("a=",a) b = yield "大饼" print("b=",b) c = yield "韭菜盒子" print("c=",c) yield "GAME OVER" gen = eat() # 获取生成器 ret1 = gen.__next__() print(ret1) ret2 = gen.send("胡辣汤") print(ret2) ret3 = gen.send("狗粮") print(ret3) ret4 = gen.send("猫粮") print(ret4)
send和__next__()区别:
1.send和next()都是让生成器向下走一次
2.send可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器代码的时候不能使用send()
生成器可以使用for循环来循环获取内的元素:
def func(): print(111) yield 222 print(333) yield 444 print(555) yield 666 gen = func() for i in gen: print(i) 结果: 111 222 333 444 555 666
二、列表推导式以及其他推导式
先看这样的代码
lst = [] for i in range(1, 15): lst.append(i) print(lst)
替换成列表推导式:
lst = [i for i in range(1, 15)] print(lst)
列表推导式是通过一行来构建你要打的列表,列表推导式看起来代码简单,但是出现错误之后很难排查。
列表推导式的常用写法:[结果 for 变量 in 可迭代对象]
我们也可以对列表中的数据进行筛选
筛选模式:[结果 for 变量 in 可迭代对象 if 条件]
# 获取1-100内所有的偶数 lst = [i for i in range(1, 100) if i % 2 == 0] print(lst)
字典推导式和集合推导式:[结果 for 变量 in 可迭代对象 if 条件]
s = {i*"胡辣汤" for i in range(10)} #集合推导式 print(s) dic = {"张无忌":"赵敏", "杨过":"小龙女", "郭靖":"黄蓉"} # 把字典中的key和value互换 # dic = {"赵敏":"张无忌", "小龙女":"杨过", "黄蓉":"郭靖"} dic1 = { v:k for k, v in dic.items()} # 强化 #字典推导式 print(dic1)
没有元组推导式,因为元组没有增删改操作
三、生成器表达式
生成器表达式和列表推导式的语法基本上是一样的. 只是把[]替换成()
gen = (i for i in range(10)) print(gen) 结果: <generator object <genexpr> at 0x106768f10>
打印的结果就是一个生成器,我们可以使用for循环来循环这个生成器:
gen = ("麻花藤我第%s次爱你" % i for i in range(10)) for i in gen: print(i)
生成器表达式也可以进行筛选:
# 获取1-100内能被3整除的数 gen = (i for i in range(1,100) if i % 3 == 0) for num in gen: print(num)
生成器表达式和列表推导式的区别:
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. 来源g1 print(list(g)) # 获取g中的数据. 这时func()才会被执行. 打印111.获取到222. g完毕 print(list(g1)) # 获取g1中的数据. g1的数据来源是g. 但是g已经取完了. g1 也就没有数据了 print(list(g2)) # 和g1同理
在Python3中提供了一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回
def gen(): lst = ["麻花藤", "胡辣汤", "微星牌饼铛", "Mac牌锅铲"] yield from lst g = gen() for el in g: print(el)
小坑: yield from是将列表中的每一个元素返回. 所以. 如果写两个yield from 并不会产生交替
的效果.
def gen(): lst = ["麻花藤", "胡辣汤", "微星牌饼铛", "Mac牌锅铲"] lst2 = ["饼铛还是微星的好", "联想不能煮鸡蛋", "微星就可以", "还可以烙饼"] yield from lst yield from lst2 g = gen() for el in g: print(el) 效果: 麻花藤 胡辣汤 微星牌饼铛 Mac牌锅铲 饼铛还是微星的好 联想不能煮鸡蛋 微星就可以 还可以烙饼
总结:推导式有,列表推导式,字典推导式,集合推导式,没有元组推导式