一. 生成器
什么是⽣成器. 生成器实质就是迭代器. 在python中有三种⽅式来获取⽣成器:
- 通过生成器函数
- 通过各种推导式来实现⽣成器
- 通过数据的转换也可以获取⽣成器
首先, 我们先看一个很简单的函数:
def func(): print("111") return 222 ret = func() print(ret) 结果: 111 222 将函数中的return换成yield就是生成器 def func(): print("111") yield 222 ret = func() print(ret) 结果: <generator object func at 0x10567ff68>
运行的结果和上面不⼀样. 为什么呢. 由于函数中存在了了yield. 那么这个函数就是一个生成器函数. 这个时候. 我们再执行这个函数的时候. 就不再是函数的执行了. ⽽是获取这个生成器. 如何使用呢? 想迭代器. ⽣成器的本质是迭代器. 所以. 我们可以直接执行__next__()来执行
以下⽣生成器.
def func(): print("111") yield 222 gener = func() # 这个时候函数不会执行. 而是获取到⽣成器 ret = gener.__next__() # 这个时候函数才会执行. yield的作用和return一样. 也是返回数据 print(ret) 结果: 111 222
那么我们可以看到, yield和return的效果是一样的. 有什区别呢? yield是分段来执⾏行行⼀一个 函数. return呢? 直接停⽌止执⾏行行函数.
接下来我们来看send方法, send和__next__()⼀样都可以让生成器执行到下一个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
⼆. 列表推导式, ⽣成器表达式以及其他推导式 ⾸先我们先看⼀下这样的代码, 给出一个列表, 通过循环, 向列表中添加1-13 :替换成列表推导式:
列表推导式是通过⼀行来构建你要的列表, 列表推导式看起来代码简单. 但是出现错误之后很难排查.
列表推导式的常⽤写法:
[ 结果 for 变量量 in 可迭代对象]
例. 从python1期到python14期写入列列表lst:
lst = [] for i in range(1, 15): lst.append(i) print(lst) lst = [i for i in range(1, 15)] print(lst) lst = ['python%s' % i for i in range(1,15)] print(lst)
筛选模式:
[ 结果 for 变量量 in 可迭代对象 if 条件 ]
⽣成器表达式和列表推导式的语法基本上是一样的. 只是把[]替换成()
打印的结果就是一个生成器. 我们可以使用for循环来循环这个生成器:
# rst = [x for x in "abc"] # # print(rst) # # lst1 = ["中岛美雪", "夏川美里", "原由子", "汪峰", "田震", "那英", "周杰伦"] # # lst2 = [x for x in lst1 if len(x) < 3] # # print(lst2) # # lst3 = [x * x for x in range(1, 21) if x % 2 == 1] # # print(lst3) # # lst4 = [3, 6, 9] # # lst5 = [(x - 2, x - 1, x) for x in lst4] # # print(lst5) # # lst6 = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], # ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] # # lst7 = [j for i in lst6 for j in i if j.count("l") == 2] # # print(lst7) # # lst8 = {"张无忌": "赵敏", "杨过": "小龙女", "郭靖": "黄蓉"} # # lst9 = {v: k for k, v in lst8.items()} # # print(lst9) #
生成器表达式和列表推导式的区别:
1. 列表推导式比较耗内存.⼀次性加载.生成器表达式几乎不占用内存.使用的时候才分配和使用内存
2. 得到的值不一样.列表推导式得到的是⼀个列表.生成器表达式获取的是⼀个生成器.
⽣成器的惰性机制: ⽣成器只有在访问的时候才取值. 说白了. 你找他要他才给你值. 不找他要. 他是不会执⾏的.
# def abc(): # print("-->",111) # yield 222 # yield 333 # # g = abc() # g1 = (x for x in g) # g2 = (x for x in g1) # # # print("g =", list(g)) # # print("g1 =", list(g1)) # # # print("g2 =", list(g2))
总结: 推导式有, 列表推导式, 字典推导式, 集合推导式, 生成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选) ⽣成器表达式可以直接获取到生成器对象. 生成器对象可以直接进行for循环. ⽣成器具有惰性机制.
一个⾯面试题. 难度系数500000000颗星:
def add(a, b): return a + b def test(): for r_i in range(4): yield r_i g = test() # 此处只有print的时候才是真正的调用了生成器,所以此处循环两次只是生成了两个生成器表达式,n值在循环的时候没有替换到生成器表达式之中。
# 所以循环两次之后的结果为:g=(add(n, i) for i in (add(n, i) for i in g)) 而第二次循环的n=10,所以把10替换掉n,结果为20,21,22,23. for n in [2, 10]: g = (add(n, i) for i in g) # print(list(g))
友情提示: 惰性机制, 不到最后不会拿值