• python 之迭代器和生成器


    迭代器和生成器

    迭代器

    • 什么是迭代器

      • 迭代器是类序列的接口。迭代器不是序列但表现出序列行为的对象, 例如: 字典的键、文件的行等等
    • 为什么要迭代器

      • 提供了可扩展的迭代器接口
      • 对列表迭代带来了性能上的增强
      • 在字典迭代中性能提升
      • 创建真正的迭代接口,而不是原来的随机对象访问
      • 与所有已经存在的用户定义的类以及扩展的模拟序列和映射的对象后兼容
      • 迭代非序列集合时,可以创建更简洁可读的代码
    • 如何迭代

      • 迭代器就是一个有 next() 方法的对象,而不是通过索引来计数。当你或一个循环机制需要下一个项时,调用迭代器的 next() 方法就可以获得它条目全部取出后,会引发一个 StopIteration 异常,这并不是表示错误发生,只是告诉外部调用者,迭代完成

      • for 循环迭代: for 循环会自动调用迭代器的 next() 方法,以及键时 StopInteration 异常

    for in seq:
    	do_something_to(i)
    
    # 实际上是这样工作的: 
    fetch = iter(seq)
    while True:
        try:
            i = fetch.next()
        expect StopIteration:
            break
        do_something_to(i)
    
    • 迭代器的限制

      • 不能向后移动,不能回到开始,也不能复制一个迭代器
      • 如果想要再次迭代同个对象,只能取创建另一个迭代器对象
    • 可变对象和迭代器

      • 一个序列的迭代器只是记录当前迭代到达第多少个元素,所以如果你在迭代是改变了元素,会更新立即反应到你所迭代的条目上
      • 在迭代一个字典的键时,你绝对不能改变这个字典。使用字典的 keys() 方法是可以的。因为 keys() 方法返回一个独立的字典的键列表。而迭代器与实际对象绑定在一起,不会继续执行下去
    • 如何创建迭代器

      • 对一个对象调用 iter() 方法就可以得到它的迭代器;

        • 如: iter(obj), iter(func, sentinel)
      • 如果传递一个参数给 iter() ,它会检查你传递的是不是一个序列,如果是,则根据索引从 0 一致迭代到序列结束。

      • 另一个创建迭代器的方法是实用类,一个实现了 iter() 和 next() 方法的类可以作为迭代器使用。

      • 如果传递两个参数给 iter() ,它会重复的调用 func,直到迭代器的下一个值等于 sentinel


    生成器表达式

    • 生成器表达式是列表解析的扩展

      • 列表解析的一个不足就是必要生成所有的数据,用以创建整个列表。这可能对有大量数据的迭代器有负面效应。
      • 生成器表达式通过结合列表解析和生成器解决了这个问题
    • 生成器表达式和列表解析非常相似,但是它并不会真正的创建数字列表,而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目"产生"(yield)出来。

    • 生成器表达式使用了"延迟计算",所以它在使用内存上更有效

    • 生成器并不会让列表解析废弃,它只是一个内存使用更友好的结构

    • 生成器表达式和列表解析:

      • 列表解析: [expr for iter_var in iterable if code_expr]
      • 生成器表达式: (expr for iter_var in iterable if code_expr)

    生成器

    • 协同程序: 是可以运行的独立函数调用,可以暂停或挂起,并从程序离开的低档继续或者重新开始

    • 生成器: 当协同程序暂停的时候,我们能从其中获得一个中间的返回值,当调用回到程序中时,能够传入额外或者改变了的参数,但仍能够从我们上次离开的地方继续,并且所有状态完整。挂起返回出中间值并多次继续的协同程序被称为生成器

    • 什么是 python 式的生成器?

      • 从语法上讲,生成器是一个带 yield 语句的函数。
      • 一个函数或者子程序只返回一次,但一个生成器能暂停执行并返回一个中间的结果——那就是 yield 语句的功能,返回一个值给掉用着并暂停执行
      • 当生成器的 next() 方法被调用的时候,它会准确的从离开的地方继续(当它返回一个值给调用者时)
    • 一个超级简单的生成器

    def func1():
        yield 1
        yield 2
        yield 3
    
    my_func = func1()
    print(f"1次next--->: {my_func.__next__()}")
    print(f"2次next--->: {my_func.__next__()}")
    print(f"3次next--->: {my_func.__next__()}")
    print(f"4次next--->: {my_func.__next__()}")
    
    # =============输出结果===========
    # 1次next--->: 1
    # 2次next--->: 2
    # 3次next--->: 3
    # Traceback (most recent call last):
    #   File "E:/MyCode/PyCode/迭代器和生成器/迭代器.py", line 10, in <module>
    #     print(f"4次next--->: {my_func.__next__()}")
    StopIteration
    
    • 加强版生成器特性
      • 加强特性: 用户可以将值送回给生成器[send()],在生成器中抛出异常,以及要求声称其退出[close()]
      • 由于双向的动作涉及到叫做 send() 的代码来向生成器发送值(以及生成器返回的值发送回来),现在 yield 语句必须是一个表达式,因为当回到生成器中继续执行的时候,你或许正在接收一个进入的对象
    def counter(start_at=0):
        count = start_at
    
        while True:
            val = (yield count)
            if val is not None:
                count = val
            else:
                count += 1
    
    my_func = counter()
    
    i = 1
    while True:
        val = my_func.__next__()
        print(f"next----->{i}: {val}")
    
        if val > 3:
            num = 10
            # send() 改变这个值回送给生成器
            my_func.send(num)
            print(f"send next----->{i}: {my_func.__next__()}")
            # 因为生成器中死循环, 想要退出, 需要调用close()方法
            my_func.close()
            break
    
        i += 1
    
    # ===================输出结果===================
    # next----->1: 0
    # next----->2: 1
    # next----->3: 2
    # next----->4: 3
    # next----->5: 4
    # send next----->5: 11
    
  • 相关阅读:
    sqlite 修改 id 自增值
    欧拉图与哈密顿图12:22
    nps 配置 vnc内网穿透
    0 范数、1 范数、2 范数有什么区别?
    相关测试
    golang学习笔记---channel(通道)
    golang学习笔记---pflag包
    Nginx+Keepalived配置Nginx自动启动和7*24不间断服务
    Keepalived+Nginx双机主备配置实践
    虚拟机中使用域名通过宿主window访问
  • 原文地址:https://www.cnblogs.com/gxfaxe/p/15196703.html
Copyright © 2020-2023  润新知