• 迭代器和生成器详解


    迭代器和生成器

    一、迭代器

    迭代是一个重复的过程,迭代器就是每次重复都是基于上次结果而继续的。这里要记住,单纯的重复并不是迭代器。迭代器主要是用来取值的。

    列表元祖等是可以利用索引进行取值,但是字典和集合是无序的,我们没有办法根据索引进行取值,要想取去字典的值就必须得用到一种不跟据索引取值的方法,这种方法就是迭代器。

    1.1 可迭代对象

    要研究迭代器,首先需要弄清楚什么是可迭代对象,在我们学过的数据类型中,有那么几个数据类型是可以被for循环的,而for循环我们之前说过,他就是对可迭代对象的循环。这就说明哪些数据类型是属于可迭代对象的。比如:字符串、列表、字典、集合、元祖。当然我们学的文件对象也是一种可迭代对象。

    可迭代对象就是在内置的方法中有__iter__的对象都是可迭代对象。

    # 可迭代对象
    s = "字符串"
    print(s.__iter__())  # 含有__iter__的内置方法就是可迭代对象
    

    1.2 迭代器对象

    那么什么又是迭代器对象呢?内置有_iter_、__next__的方法的对象都是迭代器对象,迭代器对象可以通过可迭代对象得到。

    # 可迭代对象转为迭代器对象的方法
    s = "字符串"
    s_iter = s.__iter__()  # 此时的s_iter含有__next__的内置方法,就转为了迭代器对象。
    
    # __next__:从头一次取出迭代器对象中的值。
    print(s_iter.__next__())
    print(s_iter.__next__())
    print(s_iter.__next__())
    
    # 如果迭代器对象中的值被取完之后依然继续取,就会报错。
    

    迭代器对象的值一旦被取完,就会“死亡”,不能再进行取值了,在想重新取值的话,就必须重新在赋值然后重新取。

    1.3 可迭代对象和迭代器区别

    从定义上我们就可以看出,所有的迭代器对象都是可迭代对象,但是不是所有的可迭代对象都是迭代器对象。可迭代对象用iter之后会转化为迭代器对象,迭代器对象用iter转化依然是迭代器对象本身。

    1.4 for循环的作用机制

    这时候我们就可以了解一下for循环的工作机理了。

    # while循环取字典的值。
    dic = {"1":2,"2":2}
    dic_iter = dic_iter.__iter__()
    
    while True:
        try:
            print(dic_iter.__next__())
        except StopIteration:
            break
    

    可以看出来,while循环也可以取出字典的值,但是比较麻烦,我们经常用的for循环更加的方便,那么for循环又是怎么工作的呢?

    for k in dic:
    	print(k)
    
    # 以上述为例。
    # 第一步:首先会进行dic = dic.__iter__(),将可迭代对象转化为迭代器对象
    # 第二步:进行dic.__next__的调用,得到返回值给k,然后进行代码块的操作
    # 第三步:循环第二步,直到出现StopIteration错误,对错误进行捕捉,退出循环。
    

    也就是说实际上for循环就是迭代器循环。

    1.5 迭代器的优点和缺点

    首先说一下优点:

    1. 为序列和非序列提供了一个统一的迭代取值的方式。

    2. 惰性计算:不管迭代器对象有多大,同一时刻只有一行数据存在。

    缺点也有两点:

    1. 在取得时候我们并不知道这个迭代器的长度。

    2. 取值是一次性的,过去的就让它过去,永远无法回来,除非我们在定义一个新的迭代器对象。

    二、生成器

    大白话:生成器就是自定义的迭代器。

    生成器本身就含有iter和next的内置方法,它本身就是迭代器,那么怎么定义一个生成器呢?那就需要用到yield关键字了,yiled有以下作用

    1. yield可以暂停函数的运行,不像return,可以让函数处于运行状态且不执行代码。
    2. yield可以返回值,类似于return,其值就是生成器对象。
    # next()的效果和.__next__()是一样的。
    # 当生成器遇到next()的调用开始运行,遇到yield停止执行代码,返回生成器对象,等待下次next。
    def func():
        print(11111)
        yield 11111
        print(22222)
        yield 22222
        
    func()  # 此刻这个调用方式已经不好使了
    a = func()  # 先弄一个生成器出来
    b = next(a)  # 开始执行代码打印1111,在yield处暂停执行,返回11111
    c = next(a)  # 继续执行2222,在yield暂停执行,返回22222
    d = next(a)  # 函数体代码执行完毕,没有返回值,抛出StopIteration异常结束。
    
    

    TIPS:

    如若不先根据函数造一个生成器对象,即a = func()。一直使用next(func())就是在使用一个新的生成器,永远只执行第一个yield。我懵逼在这一段时间,如果你懂请忽略。

    yield不止可以返回值,他还可以从外界接受值。

    def foo():
    	print("生成器开始运行了。")
    	while True:
    		x = yield 123  #  yield表达式
    		print(f"{x}在运行着")
            
    g = foo()  # 创造一个生成器对象
    g.send(None)  # 等同于next(g) 第一步必须向生成器传一个空值启动生成器。(必须传)
    a = g.send(5)  # 将5给变量值x,然后开始运行,直到下一个yield,然后返回yield之后的值
    
    

    在这时,yield一共分三步走,接受值给变量,运行程序,遇到下一个yield返回值。

  • 相关阅读:
    0625jQuery练习:权限基础1:查询员工对应角色、修改员工对应角色
    0624jQuery练习:三级联动—时间
    0622jQuery基础:常用属性
    0621jQuery基础:基础属性
    0621jQuery练习:三级联动
    0621jQuery练习:弹窗
    0621jQuery基础:事件
    数据库连接-登录
    javaScript中的DOM操作及数组的使用
    设置日期对象(年-月-日 时-分-秒)实现菱形的拼接
  • 原文地址:https://www.cnblogs.com/liqianxin/p/12559863.html
Copyright © 2020-2023  润新知