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

yieldreturn 的区别

  • return
    • 函数遇到return直接终止
    • return可以返回多个值,以元组形式
  • yield
    • 函数遇到return直接挂起
    • yield也可以返回多个值,以元组形式
    • yield可以把函数变成生成器,而且还支持send()传参

内置方法#

send()#

generator.send(value)

恢复执行并向生成器函数“发送”一个值。 value 参数将成为当前 yield 表达式的结果。 send() 方法会返回生成器所产生的下一个值,
或者如果生成器没有产生下一个值就退出则会引发 StopIteration 异常。 当调用 send() 来启动生成器时,它必须以 None 作为调用参数,
因为这时没有可以接收值的 yield 表达式。

send()函数调用时发生了两件事

  1. 将参数传递给yield
  2. 调用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()函数调用时发生了两件事

  1. 将 异常 或 异常和异常信息 传递给函数挂起的位置
  2. 调用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 国际」许可协议进行许可。

每天进步一点点,冲冲冲兄弟们^_^!!

posted @   Roy-Huang  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu