python生成器
生成器
背景:当我们需要大量的有规律的数据时,通过容器类型直接定义进行存放,例如列表,将会消耗大量的内存空间。
而这些数据的使用频率往往不是很高,这对内存空间来说,这是极大的浪费,因此python引入了生成器来解决这一问题。
如果你不用这些数据时,给你一个生成器对象,而不给你具体的数据;当你使用这些数据时,我们再通过生成器计算产生具体的数据。
生成器 (generator)#
通常是指生成器函数,会返回一个 生成器迭代器(generator iterator) 的函数。
它看起来很像普通函数,不同点在于其包含 yield 表达式以便产生一系列值供给 for-循环使用或是通过 next() 函数逐一获取。
Generator类继承自Iterator类,生成器类是迭代器类的子类,所以生成器也是一种迭代器。生成器创建的对象也叫生成器迭代器(generator iterator)
当函数中包含 yield 关键字时,该函数就是生成器(generator)。
# 生成器定义
def my_generator():
print('this is generator 1')
yield 1
print('this is generator 2')
yield 2
生成器迭代器(generator iterator)#
生成器 (generator) 函数所创建的对象。
当函数执行到每个 yield 会临时挂起,并记住当前位置执行状态(包括局部变量和挂起的 try 语句)。
当该 生成器迭代器 恢复时(调用next()方法),它会从离开位置继续执行(这与每次调用都从新开始的普通函数差别很大),当继续执行到下一个 yield,这时生成器将挂起,而 yield后面的的值会被返回给 next() 的调用方。
如果生成器没有产生下一个值就退出,则将引发 StopIteration 异常
# 生成器定义
def my_generator():
print('this is generator 1')
yield 1
print('this is generator 2')
yield 2
# 生成器迭代器
gt = my_generator()
res1 = gt.__next__()
print(res1)
res2 = gt.__next__()
print(res2)
res3 = gt.__next__() # 触发StopIteration异常
print(res3)
打印结果
this is generator1
1
this is generator 2
2
Traceback (most recent call last):
File "H:\python\daily_code_normal\test.py", line 12, in <module>
print(gt.__next__())
StopIteration
生成器表达式#
生成器表达式会产生一个新的生成器对象。 其句法与列表生成式和字典生成式相同,区别在于它是用()圆括号而不是用方括号或花括号括起来的。
它看起来很像普通表达式,后面带有定义了一个循环变量、范围的 for 子句,以及一个可选的 if 子句。 以下复合表达式会为外层函数生成一系列值。
generator = (i for i in range(5))
print(generator.__next__()) # 0
print(generator.__next__()) # 1
print(generator.__next__()) # 2
print(generator.__next__()) # 3
print(generator.__next__()) # 4
yield关键字#
yield 表达式在定义 生成器(generator) 函数或 异步生成器(asynchronous generator) 函数时才会用到因此只能在函数定义的内部使用。
在一个函数体内使用 yield 表达式会使这个函数变成一个生成器函数,而在一个 async def 函数的内部使用它则会让这个协程函数变成一个异步生成器函数。
例如:
def gen(): # 定义一个生成器
yield 123
async def agen(): # 定义一个异步生成器函数
yield 123
yield 和 return 的区别
- return
- 函数遇到return直接终止
- return可以返回多个值,以元组形式
- yield
- 函数遇到return直接挂起
- yield也可以返回多个值,以元组形式
- yield可以把函数变成生成器,而且还支持send()传参
内置方法#
send()#
generator.send(value)
恢复执行并向生成器函数“发送”一个值。 value 参数将成为当前 yield 表达式的结果。 send() 方法会返回生成器所产生的下一个值,
或者如果生成器没有产生下一个值就退出则会引发 StopIteration 异常。 当调用 send() 来启动生成器时,它必须以 None 作为调用参数,
因为这时没有可以接收值的 yield 表达式。
send()函数调用时发生了两件事
- 将参数传递给yield
- 调用next()方法
def echo():
print('第一次运行next()')
x = yield 1
print(x)
x = yield 2
print(x)
x = yield 3
print(x)
g = echo()
print(next(g))
g.send('a')
g.send('b')
# g.send('c') # 触发StopIteration异常,因为调用了第四次next
打印结果
第一次运行next()
1
a
b
throw()#
generator.throw(value)
generator.throw(type[, value[, traceback]])
在生成器暂停的位置引发一个异常,并返回该生成器函数所产生的下一个值 。 如果生成器没有产生下一个值就退出,则将引发
StopIteration 异常。
如果生成器函数没有捕获传入的异常,或是引发了另一个异常,则该异常会被传播给调用方。
throw()函数调用时发生了两件事
- 将 异常 或 异常和异常信息 传递给函数挂起的位置
- 调用next()方法,如果生成器没有产生下一个值就退出,引发 StopIteration 异常。
throw(异常类型)示例:
def echo():
try:
print('第一次运行next()')
yield 1
except Exception as e:
print(e) # 没有异常信息
print('第二次运行next()')
yield 2
g = echo()
print(next(g))
res = g.throw(TypeError)
print(res)
打印结果
第一次运行next()
1
第二次运行next()
2
throw(异常类型,异常信息)示例:
def echo():
try:
print('第一次运行next()')
yield 1
except Exception as e:
print(e)
print('第二次运行next()')
yield 2
g = echo()
print(next(g))
res = g.throw(TypeError, '传入的异常信息')
print(res)
打印结果
第一次运行next()
1
传入的异常信息
第二次运行next()
2
如果生成器没有产生下一个值就退出,引发 StopIteration 异常,示例
def echo():
try:
print('第一次运行next()')
yield 1
except Exception as e:
print(e)
g = echo()
print(next(g))
res = g.throw(TypeError, '传入的异常信息')
print(res)
打印结果
第一次运行next()
1
传入的异常信息
Traceback (most recent call last):
File "H:\python\daily_code_normal\day_19\main.py", line 12, in <module>
res = g.throw(TypeError, '传入的异常信息')
StopIteration
close()#
generator.close()
使用 close() 函数关闭生成器,关闭生成器后如果再调用 next() 取值,无论有没有下一个 yield ,都会引发 StopIteration 异常
def echo():
print('第一次运行next()')
yield 1
print('第二次运行next()')
yield 2
g = echo()
print(next(g))
g.close()
print(next(g))
打印结果
第一次运行next()
1
Traceback (most recent call last):
File "H:\python\daily_code_normal\day_19\main.py", line 11, in <module>
print(next(g))
StopIteration
利用生成器实现range方法#
# 传入 开始 结束 步长
def my_range(start, stop=None, step=1):
if not stop: # 如果未传入结束值
stop = start # 结束值等于开始值
start = 0 # 开始值等于0
while start < stop: # 开始值大于等于结束值时停止
yield start # 生成值
start += step # 每次开始值增加步长
生成器笔试题#
# 求和
def add(n, i):
return n + i
# 调用之前是函数 调用之后是生成器
def test():
for i in range(4):
yield i
g = test() # 初始化生成器对象
for n in [1, 10]:
g = (add(n, i) for i in g)
res = list(g)
print(res)
# A. res=[10,11,12,13]
# B. res=[11,12,13,14]
# C. res=[20,21,22,23]
# D. res=[21,22,23,24]
答案:C. res=[20,21,22,23]
# 关键代码推导
for n in [1, 10]:
g = (add(n, i) for i in g)
# 第一次循环 g = (add(n, i) for i in test())
# 第二次循环 g = (add(n, i) for i in (add(n, i) for i in test()))
res = list(g) # 当调用生成器时,生成器才开始制造数据,之前并未带入数据计算
# 所以,此时n=10,
# test()的生成的数据为0,1,2,3
# 带入数据到第二次循环最后的得到的生成器计算结果为 [20,21,22,23]
作者:ALoongH
出处:https://www.cnblogs.com/aloongh/p/17461065.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
每天进步一点点,冲冲冲兄弟们^_^!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!