一、什么是生成器
一般的迭代器对象是需要有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
网友评论