环境小硕的转行之路-16-生成器函数、推导式、生成器表达式

大纲

    1. 生成器
            生成器的本质就是迭代器
            生成器的特点和迭代器一样(3点).取值方式和迭代器一样(__next__(), send(): 给上一个yield传值).
            生成器一般由生成器函数或者生成器表达式来创建
            其实就是手写的迭代器

        2. 生成器函数
            和普通函数没有区别. 里面有yield的函数就是生成器函数.
            生成器函数在执行的时候. 默认不会执行函数体. 会返回生成器。
            通过生成器的__next__(),或者send()分段执行这个函数。
            send() 给上一个yield传值, 不能在开头(没有上一个yield), 最后一个yield也不可以用send()。

        3. 推导式
            1. 列表推导式 [结果 for循环 条件筛选]
            2. 字典推导式 {k:v for循环 条件筛选}
            3. 集合推导式 {k for循环 条件}

        4. 生成器表达式
            (结果 for循环 条件)
            特点:
                1. 惰性机制
                2. 只能向前
                3. 节省内存(老母鸡生鸡蛋vs买一车鸡蛋) 

生成器

导入

def func():
     print('呵呵呵')
     yield 1#return和yield 都可以返回数据
     print('哈哈哈')
 print(func())#返回一堆地址,说明此时不会执行函数,拿到的是生成器
 print(func().__next__())#执行到下一个yield
 ret = func()
 ret.__next__()
 ret.__next__()#Stopiteration因为没有下一个yield了
yield:相当于return,可返回数据,但是yield不会彻底中断函数,会分段执行函数。
如果函数中有yield,则此函数变成生成器(本质是迭代器).想执行生成器函数必需__next__(惰性机制),执行yield就执行到下一个yield。

 

为什么要用生成器

#不用生成器
def order():
    lst = []
    for i in range(10001):
        lst.append('衣服'+str(i))
    return lst

ll = order()#衣服放在这里暂时不用占用大量内存,内存很有可能被撑爆.最好来一个人要一件衣服,这样内存不容易被撑爆。
#用生成器
def order():
    for i in range(10000):
        yield '衣服'+str(i)
g = order()#获取生成器
guest1 = g.__next__()
guest2 = g.__next__()#这样子几乎不占内存,需要的时候直接生成。
#send()和__next__一样,执行到下一个yield,可以给上一个yield的位置传值
def func():
    print('我是第一段')
    a = yield 123#执行含有等号段的的时候先执行等号右边的,见到yield直接跑出去了.第二次跑a->yield 456,此时__next()__没有给a传值,所以a是空的.(NONE)
    print(a)#这里不知道为什么不会打印一个'none'
    print('我是第二段')
    b = yield 456
    print(b)
    print('我是第三段')
    c = yield 789
    print(c)
    print('我是第四段')
    yield 'negu'#生成器函数最后收尾的一定是yield,且不能传值
y = func()
print(y.__next__())#因为一开始没有上一个yield,所以必须用__next()__
print(y.send('嘻嘻'))
print(y.send('呼呼'))
print(y.send('湫湫'))

输出:
我是第一段
123
嘻嘻 我是第二段 456 呼呼 我是第三段 789 湫湫 我是第四段

 

获得生成器的内部元素

#生成器可以使用for循环获得其内部的元素
def shengcheng():
    yield 88
    yield 888
    yield 8888
    yield 88888
for i in shengcheng():#for的内部一定有__next__
    print(i)
print(list(shengcheng()))#list内部也有__next__

 

推导式

#推导式:用一句话来生成一个列表
lst = ['python'+str(i) for i in range(1,16)]
print(lst)
#语法:[结果+for循环]

lst = [i for i in range(1,100) if i%2 != 0]
print(lst)

#语法[结果+for循环+判断]

#100以内能被3整除数的平方
lst = [i**2 for i in range(100) 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 line in names for name in line if name.count('e') == 2]
print(lst)

#[11,22,33,44]->{0:11,1:22,2:22......}
lst = [11,22,33,44]
dict = {k:v for k,v in enumerate(lst)}
print(dict)
#语法{k:v for循环 条件筛选}

#调换dict的key和value

dict_1 = {v:k for k,v in dict.items()}
print(dict_1)

#字典里直接装数据而没有k:v格式则生成一个set

sets = {i for i in range(10)}
print(sets,type(sets))

 

生成器+推导式 = 生成器表达式

gen = ( i for i in range(10))#生成器,惰性机制

def func():
    print(111)
    yield 222
g = func()#产生了一个生成器
g1 = (i for i in g)
g2 = (i for i in g1)
print(list(g)) #[222] 从源头把数据拿走了
print(list(g1)) # 获取g1中的数据. g1的数据来源是g. 但是g已经取完了数据,g1就没有数据了。
print(list(g2)) #同上,(g1,g2,g内部对生成器取值方式都必须为__next__取值取完就没了。)

 

地狱训练

def add(a, b):#原因就是惰性机制
    return a + b
def test():
    for r_i in range(4):
        yield r_i
g = test()
for n in [2,10]:
    g = (add(n, i) for i in g)
print(list(g))#全拿出来 结果20,21,22,23
#解析
#上面的代码相当于:
# n = 2
# g  = (add(n, i) for i in g)#执行n=2的时候,此时没有从生成器中拿过值,n没有往里带。
# n = 10
# g  = (add(n, i) for i in add(n, i) for i in g)

#又相当于
# n = 10
# g = [0,1,2,3]
# t  = (add(n, i) for i in (add(n, el) for el in g))
# print(list(t))
print(list(g))#全拿出来

 

posted @ 2019-09-12 10:37  negu  阅读(147)  评论(0编辑  收藏  举报