python全栈开发 生成器 :生成器函数,推导式及生成器表达式

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]
[]
'''
上一篇:Flash性能优化


下一篇:在配置XML时报的The reference to entity "dataSource" must end with the ';' delimiter错误