1.生成器
生成器的本质是迭代器。
在python中有三种方式来获取生成器
1.通过生成器函数
2.通过各种推到式来实现生成器
3.通过数据的转换也可以获取生成器
# 函数 def func(): print('这是函数func') return '函数func' func() # 生成器 def func1(): print('这是函数func1') yield '函数func' func1() #结果:<generator object func1 at 0x0000023B3F280B48>
def foo(): # yield 关键字 print(1) yield 4 # yield 返回值 记录执行到哪里 停止 print(2) yield 5 g = foo() # <generator object foo at 0x021246C0> #产生一个生成器 print(g.__next__()) # g.__next__ 触发启动这个生成器 print(g.__next__()) # g.__next__ 触发启动这个生成器
生成器定义: 函数体中存在yield就是生成器,函数+()就产生 生成器
1.生成器 只能向下执行
2.正常函数中,函数+()调用在个函数
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__()程序报错 print(ret3) 结果: 111 222 333 444
当程序运行完最后一个yield,那么后面继续运行__next__()程序会报错。__next__ 和 yield 要一一对应, 最后的一个yield下边能写就是不运行。
生成器是一个一个的,一直向下进行,不能向上.__next__()到哪,指针就指到哪儿.下一次继续就获取指针指向的值。
生成器的好处:节省空间
#面试题 def foo(): def func(): print(1) yield 4 return func f = foo() g = f() print(g.__next__())
send方法
send和__next__()一样都可以让生成器执行到下一个yield
def eat(): for i in range(1,10000): a = yield '包子'+str(i) print('a is',a) b = yield '窝窝头' print('b is', b) e = eat() print(e.__next__()) print(e.send('大葱')) print(e.send('大蒜'))
send和__next__()区别:
1.send 和 next()都是让生成器向下走一次。
2.send可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器的时候不能使用send()
3.第一次调用的时候使用send()也可以但是send的参数必须是None
生成器可以for循环来循环获取内部元素:
def func(): yield 1 yield 2 yield 3 yield 4 yield 5 f = func() for i in f: print(i)
yield from
在python3中提供一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回
def func(): lst = ['卫龙','老冰棍','北冰洋','牛羊配'] yield from lst g = func() for i in g: print(i)
def func(): lst = ['卫龙','劳宾光','北冰洋'] lst2 = ['aa','老冰棍','北冰洋123'] yield from lst yield from lst2 g = func() for i in g: print(i) #注意:yield from 是将列表中的每一个元素返回,所以 如果写两个yield #from 并不会产生交替的效果
2.推导式
列表推导式
列表推导式的常⽤写法:
[结果 for 变量 in 可迭代对象]
ls = [i for i in range(10)] print(ls)
筛选模式
[结果 for 变量 in 可迭代对象 if 条件]
li = [i for i in range(10) if i > 3] print(li)
字典推导式
lst1 = ['1','2'] lst2 = [2,3] d = {lst1[i]:lst2[i] for i in range(2)} print(d)
集合推导式
dic = {'1':2,'3':4} d = {i for i in dic.items()} print(d)
生成器推导式: 长得像元组的推导式 其实是生成器推导式 #没有元组推导式
li = (i for i in range(100) if i>3) for i in range(10): print(li.__next__())
生成器表达式和列表推导式的区别:
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同理理 print(next(g)) print(next(g1)) print(next(g2)) # 可以用next来验证 其实list就是将内容迭代了转换成了列表
注意:生成器是要值的时候才能拿值
总结:
1.推导式有, 列表推导式, 字典推导式, 集合推导式, 没有元组推导式
2.生成器表达式: (结果 for 变量量 in 可迭代对象 if 条件筛选)
3.生成器表达式可以直接获取到⽣成器对象. ⽣成器对象可以直接进行for循环. ⽣成器具有惰性机制.
3.面试题
def add(a, b): return a + b def test(): for r_i in range(4): #[0,1,2,3] yield r_i g = test() for n in [5,10]: g = (add(n, i) for i in g) print(list(g)) # 拆解: # n = 2 # g1 = (add(n,i) for i in g) #[10,11,12,13] # n = 10 # g2 = (add(n,i) for i in g1) #[20,21,22,23] # print(list(g2)) # 结果 [20,21,22,23]