021.Python基础进阶_生成器

无奋斗不青春

我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈
入 门 教 程 推 荐 :👉👉 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 异常提示
  • 生成器只会遍历一次
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失心疯_2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值