生成器函数及表达式,列表推导式

生成器

生成器的本质就是迭代器

生成器一般由生成器函数或者生成器表达式来创建

其实就是手写的迭代器

def func():
    print("娃哈哈")
    yield 1 # return和yield都可以返回数据
    print("呵呵呵")


gen = func() # 获取生成器,不会执行你的函数. 拿到的是一个生成器
print(gen)# >>> <generator object func at 0x0000021441B634F8>
#不会拿到值,拿到的是个内存地址

生成器的特点

和迭代器一样.取值方式和迭代器一样(__next__()

send(): 给上一个yield传值).

def func():
    print("娃哈哈")
    yield 1
    print("呵呵呵")
gen = func() # 不会执行你的函数. 拿到的是生成器
print(gen.__next__()) #执行函数. 执行到下一个yield.
>>> 结果: 
娃哈哈
1

生成器函数

和普通函数没有区别. 里面有yield的函数就是生成器函数.

生成器函数在执行的时候. 默认不会执行函数体. 返回生成器

def func():
    print("娃哈哈")
    yield 1 # return和yield都可以返回数据
    print("呵呵呵")


gen = func() # 不会执行你的函数. 拿到的是生成器
print(gen)
>>>
<generator object func at 0x00000181098B34F8>

 

yield: 相当于return 可以返回数据. 但是yield不会彻底中断函数. 分段执行函数

yield好处

'''
生成器有什么好处呢?就是不会一下子在内存中生成太多数据

假如我想让工厂给学生做校服,生产2000000件衣服,我和工厂一说,工厂应该是先答应下来,然后再去生产,我可以一件一件的要,也可以根据学生一批一批的找工厂拿。
而不能是一说要生产2000000件衣服,工厂就先去做生产2000000件衣服,等回来做好了,学生都毕业了。。。
'''
def order():
    lst = []
    for i in range(200000):
        lst.append("衣服"+str(i))
    return lst
g = order()
print(g) # 这个数据太庞大了,一下处理2000000件衣服...
#我们加了yield就不一样了,我们可以根据自己的需求要衣服
def order():
    """生产衣服"""
    for i in range(2000000):
        yield "生产了第%s件衣服"%i

g =order()
print(g.__next__()) #要一件衣服
print(g.__next__()) #再要一件衣服
print(g.__next__()) #再要一件衣服
num = 0
for i in g:         #要一批衣服,比如5件
    print(i)
    num +=1
    if num == 5:
        break

通过生成器的__next__()分段执行这个函数.

send() 给上一个yield传值, 不能再开头(没有上一个yield), 最后一个yield也不可以用send()

#send() 和__next__()是一样的. 可以执行到下一个yield, 可以给上一个yield位置传值
def func():
    print("我是第一个段")
    a = yield 123
    print(a)
    print("周杰伦是第二段")
    b = yield 456
    print(b) # ??
    print("林俊杰是第三段")
    c = yield 789
    print(c)
    print("王力宏是最后一个段")
    yield 79      # 最后收尾一定是yield
g = func()
print(g.__next__()) 
print(g.__next__())
print(g.__next__())
print(g.__next__())
>>>
我是第一个段
123
None
周杰伦是第二段
456
None
林俊杰是第三段
789
None
王力宏是最后一个段
79
g = func() #获取生成器
print(g.__next__()) # 没有上一个yield 所以不能使用send() 开头必须是__next__()
print(g.send("煎饼果子")) #向第一个yield传值
print(g.send("韭菜盒子")) #向上一个yield传值
print(g.send("锅包肉"))    #向上一个yield传值 
>>>
我是第一个段
123
煎饼果子
周杰伦是第二段
456
韭菜盒子
林俊杰是第三段
789
锅包肉
王力宏是最后一个段
79

推导式

用一句话来生成一个列表

列表推导式

[结果 for循环 条件筛选]

#50以内的奇数
lst = [i for i in range(50) if i%2==1]
print(lst)
>>>
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49]

 字典推导式 {k:v for循环 条件筛选}

#key和value颠倒
dic = {"jj": "林俊杰", "jay": "周杰伦", "zs": "赵四", "ln":"刘能"}
d = {v : k for k,v in dic.items()}
print(d)
>>>
{'林俊杰': 'jj', '周杰伦': 'jay', '赵四': 'zs', '刘能': 'ln'}

 集合推导式

{k for循环 条件}

#集合有去重作用
lst = [1, 1, 4, 6,7,4,2,2]
s = { el for el in lst }
print(s)
>>>
{1, 2, 4, 6, 7} 

生成器表达式

元组tuple没有推导式,生产器表达式

(结果 for循环 条件)

tu = (i for i in range(10)) # 没有元组推导式.  生成器表达式
print(tu) # 生成器
>>>
<generator object <genexpr> at 0x00000136D23B34F8>
#既然是生成器那我们就可以用 __next__来拿值
print(tu.__next__())
print(tu.__next__())
print(tu.__next__())
print(tu.__next__())
print(tu.__next__())
print(tu.__next__())
print(tu.__next__())
>>>
0
1
2
3
4
5
6

特点:

惰性机制

只能向前

节省内存

#值2000块的面试题!!!
def func():
    print(111)
    yield 222
    yield 333
g = func() # 获取生成器
g1 = (i for i in g) # 生成器
g2 = (i for i in g1) # 生成器
print(list(g)) #  111 [222,333] 源头. 从源头把数据拿走了
# print(list(g1)) # [] 都是要从源头拿数据的,所以这里执行的时候. 源头已经被g取走了,没有了数据
# print(list(g2)) #  [] 这里也没有值了
#求和
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) # 当n = 2时 g不取值,因没有值,只是单纯遍历一下并没有执 #行,这时候 g = (add(n,),for i in g) # 当n = 10时 g = (add(n+i),for i in add(n+i),for i in g) # 因惰性机制,运行到最后 g开始取值,从源头取值,源头g = 0,1,2,3 print(list(g)) # 代数,n =10时 g =(add(10+i),for i in add(10+0,1,2,3)) # i = 10,11,12,13 # 20,21,22,23 #list(g) 一次性全拿出来,所以 list(g) 结果 20,21,22,23

 


posted @ 2018-08-13 17:14  张大仙er  阅读(323)  评论(0)    收藏  举报