迭代器和可迭代对象
由for循环的内部原理说起
            
              list01 
              
                =
              
              
                [
              
              
                2
              
              
                ,
              
              
                434
              
              
                ,
              
              
                5
              
              
                ,
              
              
                6
              
              
                ,
              
              
                8
              
              
                ]
              
              
                for
              
               item 
              
                in
              
               list01
              
                :
              
              
                print
              
              
                (
              
              item
              
                )
              
            
          
          大家有没有想过list类型对象为什么可以被for循环呢?
            
              能够被for循环的条件是:它是可迭代对象(iterable)。
            
          
          
            
              那么什么是
            
            可迭代对象
            
              呢?
            
            
             参考一下内置函数item()的官方说明文档:
          
iter(object[, sentinel])
返回一个 iterator对象。根据是否存在第二个实参,第一个实参的解释是非常不同的。如果没有第二个实参,object 必须是支持迭代协议(有 iter () 方法)的集合对象,或必须支持序列协议(有 getitem () 方法,且数字参数从 0 开始)。如果它不支持这些协议,会触发 TypeError。如果有第二个实参 sentinel,那么 object 必须是可调用的对象。这种情况下生成的迭代器,每次迭代调用它的 next () 方法时都会不带实参地调用 object;如果返回的结果是 sentinel 则触发 StopIteration,否则返回调用结果。
            由此我们可以明确知道什么是可迭代的对象:即对象实现了能返回迭代器的 iter 方法,或对象实现了 getitem 方法,而且其参数是从零开始的索引。(两者都实现也行)
            
             一文读懂Python可迭代对象、迭代器和生成器
          
判断一个对象是否是可迭代对象:
            
              
                # 看__iter__方法 或 __getitem__方法是否在__dir__字典里(用dir()内置方法),若存在就说明
              
              
                # 它是一个可迭代对象
              
              
                >>
              
              
                >
              
              
                '__iter__'
              
              
                in
              
              
                dir
              
              
                (
              
              
                [
              
              
                ]
              
              
                )
              
              
                True
              
              
                >>
              
              
                >
              
              
                '__getitem__'
              
              
                in
              
              
                dir
              
              
                (
              
              
                [
              
              
                ]
              
              
                )
              
              
                #还要进一步看其数字参数是否从 0 开始
              
              
                True
              
              
                >>
              
              
                >
              
            
          
          
            情况一:实现了能返回迭代器的__iter__方法(迭代器是具有
            
              __next__()
            
            方法的对象)。
          
            
              
                class
              
              
                SkillIterator
              
              
                :
              
              
                """
        迭代器
    """
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
               target
              
                )
              
              
                :
              
              
        self
              
                .
              
              target 
              
                =
              
               target
        self
              
                .
              
              index 
              
                =
              
              
                0
              
              
                def
              
              
                __next__
              
              
                (
              
              self
              
                )
              
              
                :
              
              
                # 如果索引越界 则抛出StopIteration异常
              
              
                if
              
               self
              
                .
              
              index 
              
                >
              
              
                len
              
              
                (
              
              self
              
                .
              
              target
              
                )
              
              
                -
              
              
                1
              
              
                :
              
              
                raise
              
               StopIteration
        
              
                # 返回下一个元素
              
              
        item 
              
                =
              
               self
              
                .
              
              target
              
                [
              
              self
              
                .
              
              index
              
                ]
              
              
        self
              
                .
              
              index 
              
                +=
              
              
                1
              
              
                return
              
               item
              
                class
              
              
                SkillManager
              
              
                :
              
              
                """
        可迭代对象
    """
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
               skills
              
                )
              
              
                :
              
              
        self
              
                .
              
              skills 
              
                =
              
               skills
    
              
                def
              
              
                __iter__
              
              
                (
              
              self
              
                )
              
              
                :
              
              
                # 创建迭代器对象  传递 需要迭代的数据
              
              
                return
              
               SkillIterator
              
                (
              
              self
              
                .
              
              skills
              
                )
              
            
          
          情况二:对象实现了 getitem 方法,而且其参数是从零开始的索引,python解释器会创建一个迭代器并尝试按顺序(从索引 0 开始)获取元素。
            
              
                class
              
              
                Eter_a
              
              
                :
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
              text
              
                )
              
              
                :
              
              
        self
              
                .
              
              sub_text
              
                =
              
              text
              
                .
              
              split
              
                (
              
              
                " "
              
              
                )
              
              
                def
              
              
                __getitem__
              
              
                (
              
              self
              
                ,
              
               index
              
                )
              
              
                :
              
              
                return
              
               self
              
                .
              
              sub_text
              
                [
              
              index
              
                ]
              
              
ob_a
              
                =
              
              Eter_a
              
                (
              
              
                "Hello world!"
              
              
                )
              
              
                for
              
               i 
              
                in
              
               ob_a
              
                :
              
              
                print
              
              
                (
              
              i
              
                )
              
            
          
          
            一般我们不用getitem去实现一个迭代器,所以重点还是放在 __iter__上好了。
            
             补充一下:标准迭代器里面大部分都同时实现了__next__和__iter__方法。所以有人这样说:
          
            
              内部含有'__iter__'并且含有"__next__"方法的对象,就是迭代器
            
          
          for循环内部原理
            实际执行情况如下图:
            
            
              
            
            
             1)调用可迭代对象的__inter__方法返回一个迭代器对象(iterator)
            
             2)不断调用迭代器的__next__方法返回元素
            
             3)知道迭代完成后,处理StopIteration异常
          
根据以上条件,我们使用while来模拟一下for循环:
            
              
                # 1. 获取迭代器对象
              
              
list01 
              
                =
              
              
                [
              
              
                2
              
              
                ,
              
              
                434
              
              
                ,
              
              
                5
              
              
                ,
              
              
                6
              
              
                ,
              
              
                8
              
              
                ]
              
              
iterator 
              
                =
              
               list01
              
                .
              
              __iter__
              
                (
              
              
                )
              
              
                #(1)先调用__iter__方法获取一个迭代器
              
              
                while
              
              
                True
              
              
                :
              
              
                try
              
              
                :
              
              
        item 
              
                =
              
               iterator
              
                .
              
              __next__
              
                (
              
              
                )
              
              
                # (2)不断调用迭代器的__next__方法
              
              
                print
              
              
                (
              
              item
              
                )
              
              
                except
              
               StopIteration
              
                :
              
              
                #(3)直到捕获StopIteration异常停止迭代
              
              
                break
              
              
                # (4)跳出循环体
              
            
          
          ,
生成器generator
            (首先它是一个函数,特殊之处在于函数使用yield返回,这样在调用此函数时会返回一个生成器对象,这是一个函数到对象的神奇过程,体现了一种惰性返回的思维(即用到才返回,不用不返回,而不是管你用不用哪怕只用一部分也一次性全部返回return)。生成器是迭代器的一种,明白了吧,想更深入理解就往下看)
            
            
              生成器及其作用
            
            
             python 生成器和迭代器有这篇就够了
          
通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator
生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。
生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器。
生成器:generator
            1.定义:能够动态(循环一次计算一次返回一次)提供数据的可迭代对象。
            
             2.作用:在循环过程中,按照某种算法推算数据,不必创建容器存储完整的结果,从而节省内存空间。数据量越大,优势越明显。
            
             3.以上作用也称之为延迟操作或惰性操作,通俗的讲就是在需要的时候才计算结果,而不是一次构建出所有结果。
          
生成器函数
            1.定义:
            
              含有yield语句的函数,返回值为生成器对象
            
            。
            
             2. 创建语法
          
            
              
                def
              
               函数名
              
                (
              
              
                )
              
              
                :
              
              
	…
	
              
                yield
              
               数据
	…
            
          
          – 调用:
            
              
                for
              
               变量名 
              
                in
              
               函数名
              
                (
              
              
                )
              
              
                :
              
              
		  语句
            
          
          3.说明:
- 调用生成器函数将返回一个生成器对象,不执行函数体。
 - yield翻译为”产生”或”生成”
 
            4.执行过程:
            
             (1) 调用生成器函数会自动创建迭代器对象。
            
             (2) 调用迭代器对象的__next__()方法时才执行生成器函数。
            
             (3) 每次执行到yield语句时返回数据,暂时离开。
            
             (4) 待下次调用__next__()方法时继续从离开处继续执行。
          
5.原理:生成迭代器对象的大致规则如下
- 将yield关键字以前的代码放在next方法中。
 - 
              将yield关键字后面的数据作为next方法的返回值。
              
(qz理解:或者说当内部调用next方法时,会跳到yield关键字之前的代码继续执行,并且将yield关键字后面的数据作为next方法的返回值,因为调试的话执行next会跳到yield关键字之前的代码。) 
内置生成器
            
              1,枚举函数enumerate
            
            
             1).语法:
          
            
              
                for
              
               变量 
              
                in
              
              
                enumerate
              
              
                (
              
              可迭代对象
              
                )
              
              
                :
              
              
     语句
              
                for
              
               索引
              
                ,
              
               元素
              
                in
              
              
                enumerate
              
              
                (
              
              可迭代对象
              
                )
              
              
                :
              
              
    语句
            
          
          2).作用:遍历可迭代对象时,可以将索引与元素组合为一个元组。
            
              2,zip函数
            
            
             1).语法:
          
            
              
                for
              
               item 
              
                in
              
              
                zip
              
              
                (
              
              可迭代对象
              
                1
              
              
                ,
              
               可迭代对象
              
                2
              
              …
              
                .
              
              
                )
              
              
                :
              
              
    		语句
            
          
          2).作用:将多个可迭代对象中对应的元素组合成一个个元组,生成的元组个数由最小的可迭代对象决定。
生成器表达式
            1.定义:用推导式形式创建生成器对象。
            
             2.语法:变量 = ( 表达式 for 变量 in 可迭代对象 [if 真值表达式] )
            
             (注意:可不要误解是元组推导式,不存在元组推导式,因为元组是不可变序列)
          
            
              
                >>
              
              
                >
              
              
                (
              
              i 
              
                for
              
               i 
              
                in
              
              
                range
              
              
                (
              
              
                1
              
              
                ,
              
              
                10
              
              
                )
              
              
                )
              
              
                <
              
              generator 
              
                object
              
              
                <
              
              genexpr
              
                >
              
               at 
              
                0x0000023A9DDA7408
              
              
                >
              
              
                >>
              
              
                >
              
              
                [
              
              i 
              
                for
              
               i 
              
                in
              
              
                range
              
              
                (
              
              
                1
              
              
                ,
              
              
                10
              
              
                )
              
              
                ]
              
              
                [
              
              
                1
              
              
                ,
              
              
                2
              
              
                ,
              
              
                3
              
              
                ,
              
              
                4
              
              
                ,
              
              
                5
              
              
                ,
              
              
                6
              
              
                ,
              
              
                7
              
              
                ,
              
              
                8
              
              
                ,
              
              
                9
              
              
                ]
              
              
                >>
              
              
                >
              
            
          
        

