Python 的可迭代对象、迭代器和生成器

系统 1368 0

迭代器和可迭代对象

由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()内置方法将生成一个列表囊括对象所有方法),若具有就说明
              
              
                # 它是一个可迭代对象
              
              
                >>
              
              
                >
              
              
                '__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循环内部原理

实际执行情况如下图:
Python 的可迭代对象、迭代器和生成器_第1张图片
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
              
              
                ]
              
              
                >>
              
              
                >
              
            
          

更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论