DAY12 生成器初始与列表生成式

初识生成器

  通过前面学习的迭代器,我们知道,迭代器十分节省内存。如果在某些情况下,我们也需要节省内存,就只能自己写,我们自己用python代码实现的迭代器就称为生成器。

  生成器:生成器就是自己用python代码写的迭代器,生成器的本质就是迭代器。

  (1)因为本质是迭代器,所以自带了__iter__方法和__next__方法。

  (2)特点:惰性运算,开发者自定义。

  python中提供的两种方法构建生成器:

  (1)生成器函数:与普通函数的return不同,而是使用yield关键字。yield语句一次返回一个结果,在每个结果中间挂起函数,以便下次从它离开的地方再次运行。

  (2)生成式推导式:类似于列表推导式,但是不是一次性构建一个列表,而是按需每次返回一个值。

 

构建生成器方式一:生成器函数

  一个包含yield关键字的函数就称为生成器函数。与普通函数的return不同,函数一遇到return就会结束函数并给函数的执行者返回值;但是yield不会结束函数,而是返回值给'生成器对象.__next__()',每次获得可迭代对象,就会推动函数的进行。

#一。回顾普通函数与return
def func1(x):
    x +=1
    return x         #函数遇到return关键字,就会结束函数并返回值给函数的执行者。

res = func1(6)   #函数的执行命令,并且接受函数的返回值
print(res)


#二。生成器函数,yield关键字。
def gen1(x):
    x +=1
    print(1111)
    yield x               #带有yield关键字的函数,称为生成器函数。
    print(2222)
    yield x

gen_obj =  gen1(5)         #此时这个就不是函数的执行者了,是生成器的对象
print(gen_obj)
>>><generator object gen1 at 0x000001DFD052FE08> 
print(gen_obj.__next__())
>>>                              #一个yield关键字,会把值返回个一个next,并且把函数挂起。
1111
6
print(gen_obj.__next__())    #第二个生成器对象,会从挂起处继续向下执行,遇到第二个yield返回。
>>>
2222
6
print(gen_obj.__next__())   #生成器本质就是迭代器,只能一路走到黑,不能回头,所以报错。
>>>
StopIteration


#生成器范例二:
def func1(x):
    x +=1
    yield x
    x +=3
    yield x
    x +=5
    yield x
g1 = func1(5)              #g1为生成器对象
print(g1.__next__())    #一个yield对应一个next,yield把值返回,所以x为6     
print(g1.__next__())    #一个yield对应一个next,yield把值返回,所以6+3=9   
print(g1.__next__())    #一个yield对应一个next,yield把值返回,所以9+5=14

'''因为生成器本质就是迭代器,所以符合迭代协议'''
for i in g1:
    print(i)
>>>
6
9
14

  总结1:yield与return的区别?

  答:return结束函数,给函数的执行者返回值。yield不会结束函数,一个next调用对应一个yield,yield把值返回给"生成器对象.__next__()"

  总结2:生成器与迭代器的差别?

  答:从内存级别来看,迭代器需要可迭代对象来进行转化,可迭代对象非常占内存;生成器直接创建,不需要转换,从本质上就节省内存。

send与next

  send : send与next在取值上是一样的,执行一个yield方法。但是send可以给上一个yield传递值。

  谨记:(1)第一次取值永远都是next。

     (2)最后一个yield永远也得不到send传的值。

#一。send与next在取值上是一样的,都是对应一个yield关键字。
def func1():
    print(1)
    yield 6
    print(2)
    yield 7
    print(3)
    yield 8

g1 = func1()
print(next(g1))
print(g1.send('alex'))
print(next(g1))
>>>
1
6
2
7
3
8

#二。但是send可以给上一个yield传递值
def func1():
    count = yield 6
    print(count)
    yield 7
    yield 8

g1 = func1()
print(next(g1))     #遇到next调用一个yield,此时yield返回值为6,所以next(g1)为6,yield无实体值,
                             所以count为None
g1.send('alex')     #遇到send取值与next一致,对一个yield,但是会给上一个yield赋值,所以 
                             count=‘alex
print(next(g1))     #同上。
>>>
6
alex
8

 

构建生成器方式二:生成器推导式

   前提知识:列表生成式/列表推导式

  1.一行代码几乎搞掂需求简单的任何列表,方便。

  2.不易排错,不建议超过三次循环。

#一。循环模式 [ 变量 for 变量 in iterable ]
list = [x for x in range(101)]
print(list)
>>>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]

l2 = [i**2 for i in range(1,11)]
print(l2)
>>>
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]


#二。筛选模式  [ 加工后的变量  for 变量 in Iterable  if  条件]
'''30以内被3整除的数'''
print([ i for i in range(1,30) if i%3==0])
>>>
[3, 6, 9, 12, 15, 18, 21, 24, 27]

'''30以内被3整除的数的平方'''
print([ i**2 for i in range(1,30) if i%3==0])
>>>
[9, 36, 81, 144, 225, 324, 441, 576, 729]

'''找出嵌套列表中名字含有两个'e'的所有名字'''
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]

print([j for i in names for j in i if j.count('e') >= 2])
>>>
['Jefferson', 'Wesley', 'Steven', 'Jennifer']

  生成器推导式

  1.十分简单,只需要把列表推导式[]换成()即可

  

# 通过生成式推导式构建生成器generator

g1 = (i for i in range(1,10000))
print(g1)
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())
>>>
<generator object <genexpr> at 0x00000253C09DFE08>
1
2
3
4

  

 

  

  

 

posted @ 2018-08-20 16:05  hehehe1994  阅读(121)  评论(0编辑  收藏  举报