生成器

作者: 小吉头 | 来源:发表于2020-05-05 21:56 被阅读0次

    一、什么是生成器

    一般的迭代器对象是需要有iter()next()方法,而生成器是一种特殊的迭代器,表面上看是没有这两个方法,不过当调用dir()去查看时还是有__iter()____next()__方法的。

    二、 产生生成器的两种方式

    1、列表生成式变成()

    a = (item * 2 for item in range(10))
    print(a) #<generator object <genexpr> at xxx>
    print(dir(a))#可以看到__iter__和__next__,说明本质是迭代器
    

    2、函数中有yield关键字

    以斐波那契为例:

    def create_num(max_num):
        a = 0
        b = 1
        current_num = 0
        while current_num < max_num:
            yield a #函数中只要有yield关键字,这个函数就不再是函数,可以理解成一个生成器模板(调用的时候产生生成器对象,对比类和对象的概念)
            a, b = b, a+b
            current_num += 1
    
    obj = create_num(3)#不会执行函数,只是创建一个生成器对象
    

    三、通过生成器生成数据

    以上面的斐波那契为例:

    使用next()迭代

    def create_num(max_num):
        a = 0
        b = 1
        current_num = 0
        print("---first----")
        while current_num < max_num:
            yield a
            a, b = b, a+b
            current_num += 1
        return "end"
    
    obj = create_num(3)
    #打印这两个对象发现地址是一样的,说明iter(obj)返回的是自己
    print(obj) 
    print(iter(obj))
    #调用next(obj)获取数据
    print(next(obj)) #从第一行代码a=0开始执行...,先打印---first---(整个代码只会打印一次---first---),执行到yield a,代码暂停执行,返回a作为当前next(obj)的结果
    print(next(obj)) #不会再从第一行代码执行,而是从yield a后面继续执行,while循环后再执行到yield a,代码暂停执行,返回a作为当前next(obj)的结果
    print(next(obj)) #从yield a后面继续执行,while循环后再执行到yield a,代码暂停执行,返回a作为当前next(obj)的结果
    try:
      print(next(obj)) #已经全部获取完,再获取会报StopIteration异常
    except StopIteration as e:
      print(e.value) #可以获取到代码最后返回的"end"字符串
    

    使用send(arg)迭代

    def create_num(max_num):
        a = 0
        b = 1
        current_num = 0
        while current_num < max_num:
            res = yield a
            print('接收的值:',res)
            a, b = b, a+b
            current_num += 1
    
    obj = create_num(3)
    
    ret = next(obj)#从第一行代码a=0开始执行,遇到yield a,代码暂停,返回a作为next(obj)的结果
    print(ret)#输出 0
    
    ret = obj.send('hello')#send方式可以传参给代码块。此时代码继续执行,参数'hello'作为yield a的结果,代码块中的res变量指向该结果,继续执行代码块中的 print('接收的值:',res),继续执行while循环直到又遇到yield a,代码暂停,返回a作为obj.send('hello')的结果
    print(ret)#代码块中输出 '接收的值:hello',这里输出 1
    
    ret = next(obj)#此时调用next(obj)不具备传参给代码块的功能,继续执行,所以yield a得到的结果是None,代码块中的res变量指向None,继续执行代码块中的 print('接收的值:',res),继续执行while循环直到又遇到yield a,代码暂停,返回a作为next(obj)的结果
    print(ret)#输出 1
    

    注意:obj = create_num(3)定义后,第一行代码用obj.send('xxx')会抛异常:TypeError: can't send non-None value to a just-started generator。因为res = yield a要在yield a返回了a之后,才能把值通过send传递给res
    使用send(None)或者next(obj)可避免异常。

    最简单的还是通过for循环迭代

    #在迭代器里面介绍过,for自动做了三个步骤,先判断obj是否可迭代,然后调用iter(obj)获取迭代器,最后调用next(获取到的迭代器)来获取元素。捕获到StopIteration异常会自动停止
    for temp in obj:
        print(temp)
    

    其他方法

    1、close()停止生成器
    def gen_func():
        yield 1
        yield 2
        yield 3
    
    
    if __name__ == "__main__":
        gen = gen_func()
        print(next(gen))
        gen.close()#生成器停止,下面再调用next(gen)迭代会抛StopIteration异常
        print(next(gen))
    
    >>>1
    >>>print(next(gen))  StopIteration
    

    close()会抛出在yield 1这行代码后面抛GeneratorExit异常(该异常继承自BaseException,定义:class GeneratorExit(BaseException)
    尝试捕获GeneratorExit异常:

    def gen_func():
        try:
            yield 1
        except GeneratorExit as e:
            print('生成器已结束')
        yield 2#因为还有yield语句,即使捕获了GeneratorExit异常,还是会抛RuntimeError异常
        yield 3
    
    
    if __name__ == "__main__":
        gen = gen_func()
        print(next(gen))
        gen.close()#生成器停止,下面再调用next(gen)迭代会抛StopIteration异常
        #print(next(gen))
    
    >>>1
    >>>生成器已结束
    >>>gen.close() RuntimeError: generator ignored GeneratorExit
    

    捕获GeneratorExit异常后,主动抛StopIteration异常,即使后面还有yield也不会报RuntimeError。正常也不需要去捕获GeneratorExit异常。

    def gen_func():
        try:
            yield 1
        except GeneratorExit as e:
            raise StopIteration
        yield 2
        yield 3
    
    
    if __name__ == "__main__":
        gen = gen_func()
        print(next(gen))
        gen.close()#生成器停止,下面再调用next(gen)迭代会抛StopIteration异常
        # print(next(gen))
    
    >>>1
    
    2、throw抛异常
    def gen_func():
        try:
            yield 1
        except Exception as e:
            print(e)
        yield 2
        yield 3
    
    
    if __name__ == "__main__":
        gen = gen_func()
        print(next(gen))
        gen.throw(Exception,'error')
    
    >>>1
    >>>error
    

    四、生成器的状态

    import inspect
    
    def gen():
        yield 1
        return "hello"
    
    if __name__ == "__main__":
        g = gen()
        print(inspect.getgeneratorstate(g))
        next(g)
        print(inspect.getgeneratorstate(g))
        try:
            next(g)
        except StopIteration:
            pass
        print(inspect.getgeneratorstate(g))
    
    >>>GEN_CREATED
    >>>GEN_SUSPENDED
    >>>GEN_CLOSED
    

    相关文章

      网友评论

          本文标题:生成器

          本文链接:https://www.haomeiwen.com/subject/ydumghtx.html