生成器

2020-05-05  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读