python 全栈开发
1.生成器函数
2.推导式
3.生成器表达式
一.生成器函数
1.生成器:
生成器的本质就是迭代器
(1)生成器的特点和迭代器一样.取值方式和迭代器一样(__next__(), send(): 给上一个yield传值).
(2)生成器一般由生成器函数或者生成器表达式来创建
(3)其实就是手写的迭代器
2.生成器函数:
(1)和普通函数没有区别. 里面有yield的函数就是生成器函数.
(2)生成器函数在执行的时候. 默认不会执行函数体. 返回生成器
(3)通过生成器的__next__()分段执行这个函数.
(4)send()给上一个yield传值,不能在开头(没有上一个yield),最后的yield后面不能用send()
例:
首先,我们先来看一个简单的函数:
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 是直接停止执行函数. 当函数执行到最后一个field的时候,如果继续执行__next__(),则函数程序会报错.
def func(): print('111') yield 222 print('333') field 444 gener = func() 这个时候函数不会执行,只会获取生成器. ret = gener.__next__() print(ret) ret = gener.__next__() print(ret) ret = gener.__next__() #最后一个yield执行完毕,再次__next__()则会报错.也就是说,和return无关了. print(ret)
例:
老男孩向 JAKE JONCES 订购了 10000套学生衣服,JAKE JONCES也很爽快,直接开了三辆卡车送来10000套衣服.
def cloth(): lst = [] for i in range(0,10000): lst.append('衣服' + str(i)) return lst cl = cloth()
那么,问题来了.老男孩现在没有10000个学生,而且又没有多余的地方来存放衣服.那怎么办那?最好的效果是我要一套,你就给我一套,给够10000套.就完美了.
def cloth(): for i in range(0,10000): yield '衣服' + str(i) cl = cloth() print(cl.__next__()) print(cl.__next__()) print(cl.__next__()) print(cl.__next__()) print(cl.__next__())
so.来看看这两种的区别: 第一种是直接一次性全都拿出来,会很占内存. 第二种是使用生成器,一次取一个,用多少取多少,生成器指向下一个,不会倒退.__next__()到哪,指针就指向哪里.下一次继续获取指针指向的值.
例:
下面我们来看 send 的方法.send()和__next__()都可以让生成器执行下一个field,但是send()会给生一个field的位置传递一个值.
def chi(): print('我吃什么') a = yield('馒头') print("a=",a) b = yield('大饼') print("b = ",b) c = yield("盖浇饭") print("c =",c) yield "GAME OVER" gen = chi() #获取生成器 ret1 = gen.__next__() print(ret1) ret2 = gen.send('包子') print(ret2) ret3 = gen.send('水饺') print(ret3) ret4 = gen.send('火腿肠') print(ret4) #我吃什么 馒头 a= 包子 大饼 b = 水饺 盖浇饭 c = 火腿肠 GAME OVER
send()与 __next__()的区别:
1.send()和__next__()都让生成器往下走一次
2.send()给上一个yield传值,不能在开头(没有上一个yield),最后的yield后面不能用send()
生成器可以使用for循环来循环获取内部的元素:
def func(): yield 1 yield 13 yield 26 yield 88 yield 46 yield 100 gen = func() for i in gen: print(i) #1 13 26 88 46 100
for i in func(): # for的内部一定有__next__()
print(i)
print(list(func())) # 内部都有__next__()
二.推导式
1.列表推导式 [结果 for循环 条件筛选]
2.字典推导式 {k:v for循环 条件筛选}
3.集合推导式 {k for循环 条件}
列表的推导式:
首先我们先看一下这样的代码, 给出一个列表, 通过循环, 向列表中添加1-13:
lst = [] for i in range(1,14): lst.appdend(i) print(lst) # [1,2,3,4,5,6,7,8,9,10,11,12,13]
然后我们转换成推导式方式:
lst = [i for i in range(1,14)] print(lst) #[1,2,3,4,5,6,7,8,9,10,11,12,13]
例:
从python第一期写到第十五期:
lst = ["python" + str(i) for i in range(1,16)] print(lst) #['python1', 'python2', 'python3', 'python4', 'python5', 'python6', 'python7', 'python8', 'python9', 'python10', 'python11', 'python12', 'python13', 'python14', 'python15']
我们还可以对列表里的数据进行筛选:
例: 100以内的奇数:
lis = [i for i in range(100) if i % 2 ==1] print(lis)
100以内能被3整除的数的平方:
lis = [i*i for i in range(100) if i % 3 == 0] print(lis)
寻找名字中带有两个e的人的名字:
names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,'Joe'],[ 'Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry' ]]
for循环:
lst = [] for line in names: for name in line: if name.count('e') == 2: print(name) #Jefferson Wesley Steven Jennifer
推导式:
lst = [name for line in names for name in line if type(name) == str and name.count("e") == 2] print(lst) #Jefferson Wesley Steven Jennifer
字典的推导式:
例:dic = {"jj":"林俊杰","jay":"周杰伦","ln":"刘能","zs":"赵四"}
d = {v:k for k,v in dic.items()} print(d) #{'林俊杰': 'jj', '周杰伦': 'jay','刘能': 'ln', '赵四': 'zs'}
集合的推导式: 可去重复
例:lst = [1, 1, 4, 6,7,4,2,2]
s = {el for el in lst} print(s) #{1, 2, 4, 6, 7}
元组没有推导式.
三.生成器表达式
结构: (结果 for循环 条件 )
特点:
1.惰性机制
2.只能向前
3.节省内存(鸡蛋和老母鸡)
生成器的表达式和列表推导式的语法基本上一致,只是把[]换成了()
例;
gen = (i for i in range(10)) print(gen) # <generator object <genexpr> at 0x106768f10>
tu = (i for i in range(10)) # 没有元组推导式. 这是生成器表达式 print(tu) # 生成器 print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__()) print(tu.__next__())
生成器的表达式也可以进行筛选
例: 获取1-100内能被3整除的数
gen = (i for i in range(100) if i% 3 == 0) for num in gen; print(num)
生成器表达式和列表表达式的区别:
1. 列表推导式比较耗内存. 一次性加载. 生成器表达式几乎不占用内存.使用的时候才使用和分配内存
2.得到的值不一样,列表推导式的到的是一个列表,生成器表达式获取的是一个生成器.
举个例子:
同样一篮子鸡蛋. 列表推导式: 直接拿到一篮子鸡蛋. 生成器表达式: 拿到一个老母鸡. 需要鸡蛋是就给你下鸡蛋.
生成器的惰性机制:生成器只有在访问时才取值,说白了,你找他要他才给你,不找他要,他是不会执行的.
def func(): print(111) yield 222 yield 333 g = func() # 获取生成器 g1 = (i for i in g) # 生成器 g3 = func() g2 = (i for i in g3) # 生成器 print(list(g)) # ??? [222,333] 源头. 从源头把数据拿走了 print(list(g1)) # ??? [] 这里执行的时候. 源头已经没有数据 print(list(g2)) # ??? [] 这里也没有值了 ''' 111 [222, 333] [] 111 [222, 333] '''
类型: 求和
例:
def add(a, b): return a + b # 生成器函数 # 0-3 def test(): for r_i in range(4): yield r_i # 0,1,2,3 g = test() # 获取生成器 for n in [2, 10]: g = (add(n, i) for i in g) print(g) # 到最后往里面放数据就对了 print(list(g)) print(list(g)) ''' <generator object <genexpr> at 0x00000000027C10A0> [20, 21, 22, 23] [] '''