生成器和生成器函数,推倒式

一. 生成器和生成器函数

1. 生成器.

  生成器实质就是迭代器.  在python中有三种方式来获取生成器: 1. 通过生成器函数 2. 通过各种推导式来实现生成器 3. 通过数据的转换也可以获取生成器

def f1():
    print('hello,world')
    return 'good'

def f2():
    print('hello,world')
    yield 'good'  #把return换成yield就是生成器
print(f1())  #hello,world   good
print(f2())  #<generator object f2 at 0x0000027BD3169390>

  运行的结果和上面不一样, 是因为函数中存在了yield. 那么这个函数就是一个生成器函数. 这个时候再执行这个函数的时候, 就不再是函数的执行了. 而是获取这个生成器,生成器的本质是迭代器. 所以可以直接执行__next__()来执行生成器.

def f():
    print('茉莉花')
    yield '歌曲'
    print('走出非洲')
    yield '电影'
    print('雷雨')
    yield '话剧'
    print('妈妈咪呀')
    yield '音乐剧'
g = f()  #函数不会执行,只会获得函数的生成器
print(g.__next__())  #函数执行到第一个yield,和return一样,yield也有返回值,但不会结束函数.
# 茉莉花
# 歌曲
print(g.__next__())  #继续执行下一个yield
# 走出非洲
# 电影
print(g.__next__())
# 雷雨
# 话剧
print(g.__next__())
# 妈妈咪呀
# 音乐剧
print(g.__next__())  #最后一个yield执行完毕,继续__next__,会报错:StopIteration 
 

   send和__next__()一样都可以让生成器执行到下一个yield, send还能给上一个yield赋值,所以第一个不能用send,最后一个也不要传值.

def f():
    print('茉莉花')
    a = yield '歌曲'  #把yield赋值给a
    print('走出非洲')
    b = yield '电影'
    print('雷雨')
    c = yield '话剧'
    print('妈妈咪呀')
    d = yield '音乐剧'
g = f()  #获取生成器
print(g.__next__())  #第一个必须要用__next__,不能用send()
print(g.send(1))  #从左边的a开始执行,到下一个yield结束.把1的值传给a
print(g.send(2))  #从左边的b开始执行,到下一个yield结束.把2的值传给b
print(g.send(3))  #从左边的c开始执行,到下一个yield结束.把3的值传给c
# print(g.send(4))  #从左边的d开始执行,把4的值传给d,无法找到下一个yield.结果会报错.所以最后不要用send()传值

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

def f():
    print('茉莉花')
    yield '歌曲'
    print('走出非洲')
    yield '电影'
    print('雷雨')
    yield '话剧'
    print('妈妈咪呀')
    yield '音乐剧'
g = f()
for el in g:
    print(el)
# 茉莉花
# 歌曲
# 走出非洲
# 电影
# 雷雨
# 话剧
# 妈妈咪呀
# 音乐剧

二. 推倒式

1. 列表推导式

  语法: lst = [最终结果 for 变量 in 可迭代对象 if 条件]

# 1. 获取1-50内能被3整除的数
lst1 = [i for i in range(1,51) if i % 3 == 0]
print(lst1)  #[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48]

# 2. 50以内能被3整除的数的平方
lst2 = [i*i for i in range(1,51) if i % 3 == 0]
print(lst2)  #[9, 36, 81, 144, 225, 324, 441, 576, 729, 900, 1089, 1296, 1521, 1764, 2025, 2304]

# 3. 寻找名字中带有两个e的⼈的名字
names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,
'Joe'],['Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry' , 'Eva']]
lst3 = [name for first in names for name in first if name.count("e") == 2]
print(lst3)  #['Jefferson', 'Wesley', 'Steven', 'Jennifer']

2. 生成器表达式

  语法: gen = (最终结果 for 变量 in 可迭代对象 if 条件)

#10以内的偶数
gen = (i for i in range(11) if i%2==0)
print(gen)  #<generator object <genexpr> at 0x000001BC2BB29390>
print(gen.__next__())  #0
print(gen.__next__())  #2
print(gen.__next__())  #4
print(gen.__next__())  #6
print(gen.__next__())  #8
print(gen.__next__())  #10

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

  (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,但g2的数据来源是g1

print(list(g))  #获取g中的数据. 这时func()才会被执行. 打印111.获取到222. g完毕
# 111
# [222]
print(list(g1))  #获取g1中的数据. g1的数据来源是g. 但是g已经取完了. g1也就没有数据了
#[]
print(list(g2))  #获取g2中的数据. g2的数据来源是g1. 但是g1没有数据. g2也就没有数据.  
#[]

  注意:深坑==> 生成器. 要值得时候才拿值.

3. 字典推倒式

#把lst1的值作为key,与之相对应的lst2的值作为value
lst1 = ["jack", "tom", "jason", "steve"]
lst2 = ['singer', "dancer", "cook", "writer"]
dic = { lst1[i]:lst2[i] for i in range(len(lst1))}
print(dic)  #{'jack': 'singer', 'tom': 'dancer', 'jason': 'cook', 'steve': 'writer'}

4. 集合推倒式

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

lst = [1,2,1,3,4,5,6,3,3,12,10,5]
s = {i for i in lst}
print(s)  #{1, 2, 3, 4, 5, 6, 10, 12}

  总结:

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

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

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

无限的我,现在才开始绽放,从东边的第一缕阳光到西边的尽头
原文地址:https://www.cnblogs.com/huangqihui/p/9330866.html