生成器和生成器表达方式

---恢复内容开始---

一. 生成器   

 

生成器实质就是迭代器.    

在python中有三种方式来获取生成器:

1. 通过生成器函数

2. 通过各种推导式来实现生成器

3. 通过数据的转换也可以获取生成器

首先, 我们先看一个很简单的函数:

def func():
    print("这几天下雨啦")
    return "很大"
func()

这几天下雨啦
def func():
    print("这几天下雨啦")
    return "很大"
print(func())
这几天下雨啦
很大
将函数中的return换成yield就是⽣成器

def
func(): print("这几天下雨啦") yield "很大" print(func()) <generator object func at 0x00000245F33D2750>

运行的结果和上面不一样. 为什么呢. 由于函数中存在了yield. 那么这个函数就是一个生成器函数. 这个时候. 我们再执行这个函数的时候. 就不再是函数的执行了. 而是获取这个生成器. 如何使用呢? 想想迭代器. ⽣成器的本质是迭代器. 所以. 我们可以直接执行__next__()来执行.

def func():
    print("这几天下雨啦")
    yield "很大"
g=func()
print(g.__next__())
这几天下雨啦
很大
def func():
    print("德玛西亚万岁")
    yield "英雄联盟"
    print("我的大刀早已饥渴难耐了")
    yield "蛮王"
    print("屈服吧,蠢货")
    yield "无双剑姬"
g=func()
print(g.__next__())
print(g.__next__())
print(g.__next__())

德玛西亚万岁
英雄联盟
我的大刀早已饥渴难耐了
蛮王
屈服吧,蠢货
无双剑姬

def func():
    print("德玛西亚万岁")
    yield "英雄联盟"
    print("我的大刀早已饥渴难耐了")
    yield "蛮王"
    print("屈服吧,蠢货")
    yield "无双剑姬"
    print("诺克萨斯")    #最后一个yield
g=func()
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())

德玛西亚万岁
Traceback (most recent call last):
英雄联盟
我的大刀早已饥渴难耐了
蛮王
屈服吧,蠢货
  File "E:/chenwei/陈伟learning/week3/day3.py", line 65, in <module>
无双剑姬
诺克萨斯
    print(g.__next__())
StopIteration

Process finished with exit code 1

当程序运⾏完后⼀个yield. 那么后⾯面继续进行__next__()程序会报错.

 

def func():
    print("德玛西亚万岁")
    yield "英雄联盟"
    print("我的大刀早已饥渴难耐了")
    yield "蛮王"
    print("屈服吧,蠢货")
    yield "无双剑姬"
g=func()
print(func().__next__())
print(func().__next__())      **不是一个生成器
print(func().__next__())  
德玛西亚万岁
英雄联盟
德玛西亚万岁
英雄联盟
德玛西亚万岁
英雄联盟 
# return 直接返回结果. 结束函数的调用
# yield 返回结果.可以让函数分段执行
生成器作用:
def func():
    lst=[]
    for i in range(1,14):
        lst.append("学生%s" % i)
    return lst
print(func())
['学生1', '学生2', '学生3', '学生4', '学生5', '学生6', '学生7', '学生8', '学生9', '学生10', '学生11', '学生12', '学生13']

def func():
    i=1
    while i <14:
        yield "学生%s" % i
        i+=1
g=func()
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
......

学生1
学生2
学生3
学生4
学生5
学生6
......

区别: 第⼀种是直接⼀次性全部拿出来.会很占用内存. 第二种使⽤⽣成器. ⼀次就⼀个. ⽤多少⽣成多少.⽣成器是⼀个⼀个的指向下⼀个.不会回去, __next__()到哪, 指针就指到哪⼉.下⼀次继续获取指针指向的值.

 

生成器可以使用for循环来循环获取内部的元素:

def func():
    yield "剑姬"
    yield "剑豪"
    yield "剑圣"
    yield "剑魔"
g=func()
for i in g:
    print(i)
剑姬
剑豪
剑圣
剑魔

def func():
yield "剑姬"
yield "剑豪"
yield "剑圣"
yield "剑魔"
g=func()
it=g.__iter__()
while True:
try:
print(it.__next__())
except StopIteration:
break
剑姬
剑豪
剑圣
剑魔

def func():
yield "剑姬"
yield "剑豪"
yield "剑圣"
yield "剑魔"
g=func()
lst=list(g)
print(lst)
['剑姬', '剑豪', '剑圣', '剑魔']

 

接下来我们来看send⽅方法, send和__next__()⼀样都可以让生成器执⾏到下⼀个yield.

 

def func():
    print("今天看什么电影?")
    yield "动作?情节?"
    print("今天吃什么饭?")
    yield "刀削面?盖饭?"
    print("今天去哪里玩?")
    yield "网吧?游戏厅?"
g=func()
print(g.__next__())
print(g.__next__())
print(g.__next__())
今天看什么电影?
动作?情节?
今天吃什么饭?
刀削面?盖饭?
今天去哪里玩?
网吧?游戏厅?

 

def func():
    print("今天看什么电影?")
    yield "动作?情节?"
    print("今天吃什么饭?")
    yield "刀削面?盖饭?"
    print("今天去哪里玩?")
    yield "网吧?游戏厅?"
g=func()
print(g.__next__())
print(g.send(1))
print(g.__next__())
今天看什么电影?
动作?情节?
今天吃什么饭?
刀削面?盖饭?
今天去哪里玩?
网吧?游戏厅?
def func():
    print("今天看什么电影?")
    a=yield "动作?情节?"
    print(a)
    print("今天吃什么饭?")
    b=yield "刀削面?盖饭?"
    print(b)
    print("今天去哪里玩?")
    c=yield "网吧?游戏厅?"
    print(c)
g=func()
print(g.__next__())
print(g.send(1))
print(g.__next__())
今天看什么电影?
动作?情节?
1
今天吃什么饭?
刀削面?盖饭?
None
今天去哪里玩?
网吧?游戏厅?
def func():
    print("今天看什么电影?")
    a=yield "动作?情节?"
    print(a)
    print("今天吃什么饭?")
    b=yield "刀削面?盖饭?"
    print(b)
    print("今天去哪里玩?")
    c=yield "网吧?游戏厅?"
    print(c)
g=func()
print(g.__next__())
print(g.send(1))
print(g.send(2))
print(g.send(3))
Traceback (most recent call last):
今天看什么电影?
  File "E:/chenwei/陈伟learning/week3/day3.py", line 174, in <module>
动作?情节?
    print(g.send(3))
1
StopIteration
今天吃什么饭?
刀削面?盖饭?
2
今天去哪里玩?
网吧?游戏厅?
3
__next__() 可以让生成器向下执行一次
send() 也可以让生成器向下执行一次, 给上一个yield传一个值, 第一个不能用send(). 
最后一个也不要传值
def func():
    print("今天看什么电影?")
    a=yield "动作?情节?"
    print(a)
    print("今天吃什么饭?")
    b=yield "刀削面?盖饭?"
    print(b)
    print("今天去哪里玩?")
    c=yield "网吧?游戏厅?"
    print(c)
    yield "game over"
g=func()
print(g.__next__())
print(g.send(1))
print(g.send(2))
print(g.send(3))
今天看什么电影?
动作?情节?
1
今天吃什么饭?
刀削面?盖饭?
2
今天去哪里玩?
网吧?游戏厅?
3
game over

send和__next__()区别:

1. send和next()都是让生成器向下走⼀次

2. send可以给上⼀个yield的位置传递值, 不能给后⼀个yield发送值. 在第⼀次执⾏⽣
成器代码的时候不能使用send()

 

⼆. 列表推导式, 生成器表达式以及其他推导式

给出一个列表, 通过循环, 向列表中添加1-9 :
lst=[]
for i in range(1,10):
    lst.append(i)
print(lst)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

 替换成列列表推导式:

lst=[i for i in range(1,10)] print(lst) [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 列表推倒式; 最终给你的是列表
# 语法 [最终结果(变量) for 变量 in 可迭代对象]

# 1. 获取1-100内的偶数
for i in range(1,100):
    if i %2==0:
        print(i)
#推导式
lst=[i for i in range(1,100) if i%2==0]
print(lst)

[最终结果 for 变量 in 可迭代对象 if 条件]
# 获取1-100内能被3整除的数
lst=[i for i in range(1,101) if i%3==0]
print(lst)
# 100以内能被3整除的数的平⽅方
lst=[i**2 for i in range(1,101) if i%3==0  ]
print(lst)
# 寻找名字中带有两个e的⼈人的名字
names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,
'Joe'],['Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry' , 'Eva']]
lst=[name for  first in names for name in first if name.count("e")==2]
print(lst)

生成器表达式

 

g=(i for i in range(10))
print(list(g))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

g=("英雄联盟我第%s次玩" % i for i in range(10))
for i in g:
    print(i)
英雄联盟我第0次玩
英雄联盟我第1次玩
英雄联盟我第2次玩
英雄联盟我第3次玩
英雄联盟我第4次玩
英雄联盟我第5次玩
英雄联盟我第6次玩
英雄联盟我第7次玩
英雄联盟我第8次玩
英雄联盟我第9次玩

⽣成器表达式和列表推导式的区别:

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同理理

111
[222]
[]
[]

字典推导式:

 

dic={"a":"b","c":"d"}
new_dic={dic[key]:key for key in dic}
print(new_dic)
{'b': 'a', 'd': 'c'}

 

lst1=["黑旋风","及时雨","豹子头","智多星"]
lst2=["李逵","宋江","林冲","吴用"]
dic={lst1[i]:lst2[i]  for i in range(len(lst1))}
print(dic)
{'黑旋风': '李逵', '及时雨': '宋江', '豹子头': '林冲', '智多星': '吴用'}

集合推导式:    

集合推导式可以帮我们直接⽣成⼀个集合. 集合的特点: ⽆序, 不重复. 所以集合推导式自带去重功能

 

lst=["剑圣","德玛","剑圣","剑姬","诺手","诺手","剑魔"]
s={i for i in lst}
print(s)
{'剑魔', '诺手', '剑姬', '德玛', '剑圣'}

 

总结:

推导式有:列表推导式, 字典推导式, 集合推导式, 没有元组推导式   

生成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选)    

生成器表达式可以直接获取到生成器对象. 生成器对象可以直接进行for循环. 生成器具有惰性机制.惰性机制, 不到后不会拿值

 

def add(a, b):
    return a + b
def gen():
    for i in range(4):
         yield i
g = gen()
for n in [2, 10]:
    '''
    n=2时
    g = (add(n, i) for i in gen())
    n=10时
    g = (add(n, i) for i in (add(n, i)  for i in gen()))
    i=0,1,2,3
    所以: n=10 i=0,1,2,3 <=>a+b=n+i=[10,11,12,13]
    g = (add(n, i) for i in [10,11,12,13]
    n=10 i=[10,11,12,13]
    g=n+i=[20, 21, 22, 23]
    '''
    g = (add(n, i) for i in g)
print(list(g))
[20, 21, 22, 23]
def add(a, b):
    return a + b
def gen():
    for i in range(4):
         yield i
g = gen()
for n in [2, 10,5]:
    '''
    n=2时
    g = (add(n, i) for i in gen())
    n=10时
    g = (add(n, i) for i in (add(n, i)  for i in gen()))
    n=5时
    g = (add(n, i) for i in add(n, i) for i in (add(n, i)  for i in gen()))
    i=0,1,2,3
    所以: n=5 i=0,1,2,3 <=>a+b=n+i=[5,6,7,8]
    g = (add(n, i) for i in add(n, i) for i in [5,6,7,8]
    n=5 i=[5,6,7,8]
    g = (add(n, i) for i in [10,11,12,13]
    n=5 i=[10,11,12,13]
    g=n+i=[15,16,17,18]
    '''
    g = (add(n, i) for i in g)
print(list(g))
[15, 16, 17, 18]

 

 

 

 

 
 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

---恢复内容结束---

posted @ 2018-07-18 15:14  chenyibai  阅读(460)  评论(0编辑  收藏  举报