环境小硕的转行之路-16-生成器函数、推导式、生成器表达式
大纲
1. 生成器
生成器的本质就是迭代器
生成器的特点和迭代器一样(3点).取值方式和迭代器一样(__next__(), send(): 给上一个yield传值).
生成器一般由生成器函数或者生成器表达式来创建
其实就是手写的迭代器
2. 生成器函数
和普通函数没有区别. 里面有yield的函数就是生成器函数.
生成器函数在执行的时候. 默认不会执行函数体. 会返回生成器。
通过生成器的__next__(),或者send()分段执行这个函数。
send() 给上一个yield传值, 不能在开头(没有上一个yield), 最后一个yield也不可以用send()。
3. 推导式
1. 列表推导式 [结果 for循环 条件筛选]
2. 字典推导式 {k:v for循环 条件筛选}
3. 集合推导式 {k for循环 条件}
4. 生成器表达式
(结果 for循环 条件)
特点:
1. 惰性机制
2. 只能向前
3. 节省内存(老母鸡生鸡蛋vs买一车鸡蛋)
生成器
导入
def func(): print('呵呵呵') yield 1#return和yield 都可以返回数据 print('哈哈哈') print(func())#返回一堆地址,说明此时不会执行函数,拿到的是生成器 print(func().__next__())#执行到下一个yield ret = func() ret.__next__() ret.__next__()#Stopiteration因为没有下一个yield了 yield:相当于return,可返回数据,但是yield不会彻底中断函数,会分段执行函数。 如果函数中有yield,则此函数变成生成器(本质是迭代器).想执行生成器函数必需__next__(惰性机制),执行yield就执行到下一个yield。
为什么要用生成器
#不用生成器 def order(): lst = [] for i in range(10001): lst.append('衣服'+str(i)) return lst ll = order()#衣服放在这里暂时不用占用大量内存,内存很有可能被撑爆.最好来一个人要一件衣服,这样内存不容易被撑爆。 #用生成器 def order(): for i in range(10000): yield '衣服'+str(i) g = order()#获取生成器 guest1 = g.__next__() guest2 = g.__next__()#这样子几乎不占内存,需要的时候直接生成。 #send()和__next__一样,执行到下一个yield,可以给上一个yield的位置传值 def func(): print('我是第一段') a = yield 123#执行含有等号段的的时候先执行等号右边的,见到yield直接跑出去了.第二次跑a->yield 456,此时__next()__没有给a传值,所以a是空的.(NONE) print(a)#这里不知道为什么不会打印一个'none' print('我是第二段') b = yield 456 print(b) print('我是第三段') c = yield 789 print(c) print('我是第四段') yield 'negu'#生成器函数最后收尾的一定是yield,且不能传值 y = func() print(y.__next__())#因为一开始没有上一个yield,所以必须用__next()__ print(y.send('嘻嘻')) print(y.send('呼呼')) print(y.send('湫湫')) 输出: 我是第一段 123
嘻嘻 我是第二段 456 呼呼 我是第三段 789 湫湫 我是第四段
获得生成器的内部元素
#生成器可以使用for循环获得其内部的元素 def shengcheng(): yield 88 yield 888 yield 8888 yield 88888 for i in shengcheng():#for的内部一定有__next__ print(i) print(list(shengcheng()))#list内部也有__next__
推导式
#推导式:用一句话来生成一个列表 lst = ['python'+str(i) for i in range(1,16)] print(lst) #语法:[结果+for循环] lst = [i for i in range(1,100) if i%2 != 0] print(lst) #语法[结果+for循环+判断] #100以内能被3整除数的平方 lst = [i**2 for i in range(100) if i%3 == 0] print(lst) # 寻找名字中带有两个e的人的名字 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] lst = [name for line in names for name in line if name.count('e') == 2] print(lst) #[11,22,33,44]->{0:11,1:22,2:22......} lst = [11,22,33,44] dict = {k:v for k,v in enumerate(lst)} print(dict) #语法{k:v for循环 条件筛选} #调换dict的key和value dict_1 = {v:k for k,v in dict.items()} print(dict_1) #字典里直接装数据而没有k:v格式则生成一个set sets = {i for i in range(10)} print(sets,type(sets))
生成器+推导式 = 生成器表达式
gen = ( i for i in range(10))#生成器,惰性机制 def func(): print(111) yield 222 g = func()#产生了一个生成器 g1 = (i for i in g) g2 = (i for i in g1) print(list(g)) #[222] 从源头把数据拿走了 print(list(g1)) # 获取g1中的数据. g1的数据来源是g. 但是g已经取完了数据,g1就没有数据了。 print(list(g2)) #同上,(g1,g2,g内部对生成器取值方式都必须为__next__取值取完就没了。)
地狱训练
def add(a, b):#原因就是惰性机制 return a + b def test(): for r_i in range(4): yield r_i g = test() for n in [2,10]: g = (add(n, i) for i in g) print(list(g))#全拿出来 结果20,21,22,23 #解析 #上面的代码相当于: # n = 2 # g = (add(n, i) for i in g)#执行n=2的时候,此时没有从生成器中拿过值,n没有往里带。 # n = 10 # g = (add(n, i) for i in add(n, i) for i in g) #又相当于 # n = 10 # g = [0,1,2,3] # t = (add(n, i) for i in (add(n, el) for el in g)) # print(list(t)) print(list(g))#全拿出来