生成器

生成器

为什么需要生成器:

假如现在有一个需求,我要打印从1-1亿的整形。如果我们采用普通的方式,直接调用range函数,那么程序肯定会崩溃,因为range(1,100000000)函数直接产生一个从1-1亿的列表,这个列表中的所有数据都是存放在内存中的,会导致内存爆满。这时候我们可以采用生成器来解决这个问题,生成器不会一次性把所有数据都加载到内存中,而是在循环的时候临时生成的,循环一次生成一个,所以在程序运行期间永远都只会生成一个数据,从而大大节省内存。

解决打印1-1亿的问题:

range函数配合圆括号可以产生一个生成器:

# 普通的列表
num_list = [x for x in range(1, 100000000)]
print(type(num_list))

# 生成式
num_gen = (x for x in range(1, 100000000))
print(type(num_gen))

//<class 'list'>
//<class 'generator'>

 

next函数和__next__方法:

next函数可以迭代生成器的返回值

自己写生成器:

生成器可以通过函数产生。如果在一个函数中出现了yield表达式,那么这个函数将不再是一个普通的函数,而是一个生成器函数。yield一次返回一个结果,并且会冻结当前函数的状态。以下是一个非常简单的生成器:

    def my_gen():
        yield 1
        yield 2
        yield 3

那么想要获取里面的值,可以通过以下方式获取:

    ret = my_gen()
    print(next(ret))
    print(next(ret))
    print(next(ret))

 

send方法:

send方法和next方法类似,可以用来触发生成器的下一个yield,但是send不仅可以触发下一个yield,还可以发送数据过去,作为yield表达式的值。

    def my_gen(start):
        while start < 10:
            temp = yield start
            print(temp)
            start += 1


    ret = my_gen(1)
    # next(ret)返回1
    # 以下结果会打印1
    print(next(ret))
    # send("Hello World")返回2
    # 但是会将"Hello World"赋值给yield start
    # 因此temp = "Hello World"
    # 那么会打印"Hello World"
    # 并且打印2
    print(ret.send('Hello World'))

 

生成器中的return语句会触发StopIterator异常:

    def my_gen(start):
        while start < 3:
            temp = yield start
            # print(temp)
            start += 1
            # 触发StopIterator异常,会导致不能继续遍历
            return "nihao"

    ret = my_gen(1)
    for x in ret:
        print(x)

 

生成器的使用案例:

  1. yield实现斐波拉契数列: 斐波拉契数列的算法:除第一个和第二个数以外,任意一个数都可由前两个数相加得到:1,1,2,3,5,8,13,21,34,55...
  2. yield做多任务切换:

     def neteasy_music(duration):
         c_time = 0
         while c_time < duration:
             print('播放音乐 %d分钟'%c_time)
             yield None
             c_time += 1
    
     def youku_movie(duration):
         c_time = 0
         while c_time < duration:
             print('播放电影 %d分钟' % c_time)
             yield None
             c_time += 1
    
     def main():
         music = neteasy_music(10)
         qq = youku_movie(10)
         music_stop = False
         movie_stop = False
         while True:
             try:
                 next(music)
             except StopIteration:
                 print('音乐播放完毕')
                 music_stop = True
    
             try:
                 next(qq)
             except StopIteration:
                 print('电影播放完毕')
                 movie_stop = True
    
             if music_stop and movie_stop:
                 break

     

  3. yield做一个简单的协程:

     from urllib.request import urlretrieve
    
     def Consumer(url):
         while True:
             if url:
                 filename = url.split('/').pop()
                 url = yield urlretrieve(url,filename='images/'+filename)
                 print('consumer')
    
     def Producter(consumer):
         # 先执行一下,让后面的send不会报错
         index = 2
         next(consumer)
         while index < 10:
             print('生产者已经获取了第%d个图片链接' % index)
             url = 'http://127.0.0.1:9000/%d.jpg' % index
             consumer.send(url)
             print('producter')
             index += 1
    
         consumer.close()
    
     con = Consumer("http://127.0.0.1:9000/1.jpg")
     Producter(con)

     

posted @ 2021-03-06 17:06  大碗炸酱面  阅读(44)  评论(0编辑  收藏  举报