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('')
return 222
ret = func()
print(ret) #
222
将函数中的return 换成yield 就是生成器.
def func():
print("")
yield 222
ret = func()
print(ret) 结果:
<generator object func at 0x10567ff68> # 只能的到生成器的内存地址
这个函数中带有yield 所以这个函数就成了生成器函数.生成器本质就是迭代器,所以我们可以执行__next__()来执行这个生成器.
def func():
print("")
yield 222
gener = func() # 这个时候函数不会执行. 而是获取到生成器
ret = gener.__next__() # 这个时候函数才会执行. yield的作用和return一样. 也是返回数据print(ret)
结果:
111
222
yield 和 return 的效果是一样的,但是yield 是分段执行一个函数,而 return 是直接停止执行函数. 当函数执行到最后一个field的时候,如果继续执行__next__(),则函数程序会报错.
def func():
print('')
yield 222
print('')
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) #
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]
[]
'''