python学习笔记 day14 生成器进阶

生成器的本质就是迭代器,因为生成器含有__iter__()方法和__next__()方法; 

带有yield关键字的函数都是生成器函数,生成器函数被调用时会返回一个生成器,但是函数体内的代码不会被执行,只有生成器调用__next__()方法时,才会被执行但是遇到yied关键字处,函数就暂停,等下一次该生成器再调用.__next__()函数时,下面的代码才会被继续执行;

 

其实生成器效率还是比较高的(个人理解),比如我们现在需要打印“哈哈哈哈哈”,需要打印200万,如果使用list存储,然后再打印,简直太浪费内存了,我们不可能一次存储完所有之后,再一次性全部打印,就好像QQ聊天,我们 输入一段内容,及时发送给i对方,而不是等所有内容编辑完成一次性发给别人;

def generator():
    for i in range(2000000):
        yield "哈哈哈哈%d"%i
g=generator()
for i in g:
    print(i)

运行结果:

生成器就是边调用边取值~

生成器函数获取值的三种方法:

1.可以使用.__next__()方法调用来获取一个值;

2.也可以使用for循环,来打印出所有的值;

3.当然也可以使用强制类型转换(比如list(generator)就会打印出生成器所有的值)-------占用内存

写一个生成器,实现:有一个文件,从文件中分段读取内容可以一行一行读,也可以几个字节读取,要求读出来的结果前面加上*****在返回给调用者;

def generator():
    with open("info",mode='r',encoding='utf-8') as file:
        while True:
            line=file.readline()
            # line=file.read(20)
            if line.strip():
                yield "*****"+line.strip()

g=generator()
for i in g:
    print(i)

运行结果:

 

 send()方法

讲send()方法之前,先来看一段代码:

def generator():
    print('a')
    yield 1
    print('b')
    yield 2
    print('3')
g=generator()
print(g.__next__())
print(g.__next__())
print(g.__next__())

运行结果:

虽然最后一个yield后面的代码print(3)被打印了,可是却报错了,因为执行了三个.__next__() 然而生成器函数内部只有两个yield可以被__next__()方法调用~

所以一般不要再最后一个yield后面写代码,会报错;

但是使用for 循环打印却不会报错,而且还可以输出最后一个yield后面的代码呢~

def generator():
    print('a')
    yield 1
    print('b')
    yield 2
    print('3')
g=generator()
for i in g:
    print(i)

运行结果:

 

 现在来看send()方法:

def generator():
    print('a')
    yield 1
    print("b")
    yield 2
    
g=generator()
print(g.__next__())
print(g.send(None))

运行结果:

1. 从运行结果上来看,send()方法和.__next__()方法是一样的,都是可以从上一个yield位置之后的代码开始执行,遇到下一个yield就停止,将值返回给g.__next__()或者g.send()处,以便打印;

def generator():
    print('a')
    value=yield 1
    print(value)
    print('b')
    yield 2
g=generator()
print(g.__next__())
print(g.send('我可以把值返回给上一个yield停止的位置处'))

运行结果:

 

执行过程是这样的:首先g.__next__()方法会从函数体开始执行,然后遇到第一个yield就停止,然后g.send()方法开始执行,从上一个yield停止的位置开始,然后把值传给上一个yield停止的位置也就是value(因为value=yield 1遇到赋值号,先执行等号右侧,然后在进行赋值,__next__()方法刚好执行到等号右侧就停止了,send()方法接着从停止的位置执行,就会把里面的值传递给赋值号左边,然后继续执行后面的代码,知道遇到下一个yield就停止了,将yield的结果返回,这一点跟__next__()方法很像~)

 

也就是   2.send()方法除了可以跟__next__()方法一样从上一个yield停止的地方开始执行,一直到下一个yield的地方停止外,还可以对上一个yield停止的位置处传数据!!

 最后需要注意的是,3.生成器在取值时,第一个值必须由.__next__()方法获得;最后一个yield不传值!!因为你传值,后面也没有yield了 也就没法用__next__()方法或者send()方法取值了

除非:

def generator():
    print('a')
    value1=yield 1
    print('send()方法给在第一个yield处传递的值',value1)
    print('b')
    value2=yield 2
    print("send()在第二个yield处传递的值",value2)
    yield
g=generator()
print(g.__next__())
print(g.send('哈哈哈'))
g.send('嘻嘻嘻')

运行结果:

 

posted @ 2018-09-04 20:55  写的BUG代码少  阅读(145)  评论(0编辑  收藏  举报