我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈
入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈
虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈
PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈
Oracle数据库教程:👉👉 Oracle数据库文章合集 👈👈
优 质 资 源 下 载 :👉👉 资源下载合集 👈👈
生成器(generator)
概念
- 生成器是一个特殊的迭代器(迭代器的抽象层级更高)
- 迭代器特性
- 惰性计算数据,节省内存
- 能够记录状态,通过next()函数访问下一个状态
- 具备可迭代特性
- 如果要打造一个自己的迭代器,那是非常复杂的
- 需要实现很多方法,后续在“面向对象”编程中会进行讲解
- 所以,就有一个更加简便的方式:“生成器”
创建方式
方式一:生成器表达式
- 把列表推导式的
[]
修改成()
- 示例
# 列表推导式 l = [i for i in range(1, 21) if i % 2 == 0] print(l) # --> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] # 生成器表达式 g = (j for j in range(1, 21) if j % 2 == 0) print(g) # --> <generator object <genexpr> at 0x000001FB3189D510>
方式二:生成器函数
- 包含
yield
语句的函数,这个函数的执行结果就是“生成器” yield
可以阻断当前函数执行,当使用next()
函数或者__next__()
都会让函数继续执行;当执行到下一个yield
语句又会暂停执行def test(): print('开始') yield 1 print('a') yield 2 print('b') yield 3 print('c') yield 4 print('d') yield 5 print('结束了') g = test() print(g) # <generator object test at 0x000002036582D510> print(next(g)) # --> 开始 # --> 1 print(next(g)) # --> a # --> 2 print(g.__next__()) # --> b # --> 3
数据访问方式
-
方式一:
next(生成器)
- 通过
next()
函数取值,超出生成器范围,会报错StopIteration
- 无法执行最后一个
yield
语句之后的代码
def test(num): for i in range(1, num + 1): yield i g = test(5) print(next(g)) # --> 1 print(next(g)) # --> 2 print(next(g)) # --> 3 print(next(g)) # --> 4 print(next(g)) # --> 5 print(next(g)) # --> 报错:StopIteration
- 通过
-
方式二:
生成器.__next__()
- 通过
.__next__()
方法取值,超出生成器范围,会报错StopIteration
- 无法执行最后一个
yield
语句之后的代码
def test(num): for i in range(1, num + 1): yield i g = test(5) print(g.__next__()) # --> 1 print(g.__next__()) # --> 2 print(g.__next__()) # --> 3 print(g.__next__()) # --> 4 print(g.__next__()) # --> 5 print(g.__next__()) # --> 报错:StopIteration
- 通过
-
方式三:
for 变量 in 生成器:
- 通过
for...in...
方式取值,取完所有值后会自动结束,不会报错 - 可以执行最后一个
yield
语句之后的代码,完整的执行完整个函数
def test(num): for i in range(1, num + 1): yield i g = test(5) for i in g: print(i) # -----输出结果----- 1 2 3 4 5
- 通过
-
方式四:
send(默认值)
send
方法启动生成器后,发送数据至生成器中,作为上一个yield
语句的返回值- 无法执行最后一个
yield
语句之后的代码
def test(): for i in range(1, 11): res = yield i print('res:', res) g = test() print(next(g)) print(g.send('xxxx')) print(g.send('oooo'))
send方法详解
- 说明
- 生成器除了使用
next
函数启动,还可以使用send
方法启动
- 生成器除了使用
- 特点
send
方法启动生成器后,发送数据至生成器中,作为上一个yield
语句的返回值
- 应用场景
- 当生成器生成一个新的值时,通过
send
方法传递一个新的参考值给上一次被挂起的yield
语句做返回值,然后根据这个参考值去做事情
- 当生成器生成一个新的值时,通过
- 区别
- 通过示例看一下
next
函数和send
方法的区别
# 使用next函数 def test(): for i in range(1, 11): res = yield i print('res:', res) g = test() print(next(g)) print(next(g)) print(next(g)) # -----输出结果----- 1 res: None 2 res: None 3 # ====================================== # 使用send函数 def test(): for i in range(1, 11): res = yield i print('res:', res) g = test() print(next(g)) print(g.send('xxxx')) print(g.send('oooo')) # -----输出结果----- 1 res: xxxx 2 res: oooo 3
- 通过示例看一下
- 执行流程解析
- 解下案例
def test(): for i in range(1, 11): res = yield i print('res:', res) g = test() print(next(g)) print(g.send('xxxx')) print(g.send('oooo'))
- 执行流程
- 第一次:调用
next
函数,程序执行到yield
关键字处暂停,并将局部变量i的值作为函数的返回值返回。返回值为:1 - 第二次:调用
send
方法,程序从上一次yield
关键字暂停处开始执行,并将send
函数传过来的参数xxxx
赋值给局部变量res,然后程序继续往下执行,打印res的值,直到遇到了yield
关键字,程序暂停,并将局部变量i的值作为函数的返回值返回。返回值为:2 - 第三次:调用
send
方法,程序从上一次yield
关键字暂停处开始执行,并将send
函数传过来的参数oooo
赋值给局部变量res,然后程序继续往下执行,打印res的值,直到遇到了yield
关键字,程序暂停,并将局部变量i的值作为函数的返回值返回。返回值为:3
- 第一次:调用
- 注意事项
- 第一次直接使用
send(参数)
方法启动生成器会报错
TypeError: can't send non-None value to a just-started generator
- 原因:
send
方法启动生成器后,发送数据至生成器中,作为上一个yield
语句的返回值- 第一次启动生成器的时候,程序会从第一行执行,执行到第3行的时候会先将i作为函数返回值返回,然后暂停执行,再将
send()
方法的参数传递给上一个yield
语句作为返回值,但是这个时候没有上一个yield
语句,所以就报错了
- 解决方法
- 方法一:第一次启动生成器,使用
next()
函数(建议使用该方法解决) - 方法二:第一次启动生成器,使用
send()
方法,但是参数传递None
,即生成器.send(None)
- 方法一:第一次启动生成器,使用
close方法
- 调用
close()
方法,会阻止生成器函数继续执行,该函数会在程序停止运行的位置抛出GeneratorExit
异常(生成器退出异常)def test(): try: yield 1 except GeneratorExit: print('捕获到 GeneratorExit') print('捕获到异常之后执行....') g = test() print(g.__next__()) g.close() # -----输出结果----- 1 捕获到 GeneratorExit 捕获到异常之后执行....
- 异常捕获图解
-
-
通过
g.__next__()
方法启动生成器,会执行到第3行,返回yield关键字后面的值:1,然后暂停 -
执行
g.close()
语句,此时函数暂停在第3行,close()
函数在此行停止运行,并且抛出GeneratorExit
异常 -
通过
tyr
捕获异常,然后继续往后执行,如果此处不捕获异常,程序则会直接退出
-
- 生成器函数一旦使用
close()
函数停止运行,后续将无法再调用next()
函数或者__next__()
方法启动执行,否则会抛出StopIteration
异常def test(): yield 1 yield 2 yield 3 g = test() print(g.__next__()) print(g.__next__()) g.close() print(g.__next__()) # -----输出结果----- 1 2 Traceback (most recent call last): File "E:\Projecs\Python\11-函数\08-生成器.py", line 129, in <module> print(g.__next__()) StopIteration
throw方法
throw()
方法是向生成器发送一个异常,可以结束系统定义的异常(当然包括自定义的异常)- 可以通过
try
语句捕获这个异常,然后继续执行后续代码,直到遇到下一个yield
语句,返回yield
语句的值,并暂停执行 - 如果不通过 try 语句捕获异常,系统将直接报错,退出运行,后续代码将不再执行
def test(): try: yield 1 except ValueError: print('捕获了异常') yield 2 yield 3 g = test() print(g.__next__()) print(g.throw(ValueError)) # -----输出结果----- 1 捕获了异常 2
- 一开始生成器函数在
yield 1
处暂停执行,当执行throw()
方法时,它会先抛出ValueError
异常,通过try
语句捕获异常后,继续执行后续代码直到下一个yield
语句,此时返回2 - 如果不通过
try
语句捕获异常,后续代码将不再执行
def test(): yield 1 yield 2 yield 3 g = test() print(g.__next__()) print(g.throw(ValueError)) # -----输出结果----- 1 Traceback (most recent call last): File "E:\Projecs\Python\11-函数\08-生成器.py", line 158, in <module> print(g.throw(ValueError)) File "E:\Projecs\Python\11-函数\08-生成器.py", line 150, in test yield 1 ValueError
- 一开始生成器函数在
注意事项
- 生成器函数中如果碰到
return
语句,会直接终止,并且抛出StopIteration
异常提示 - 生成器只会遍历一次