当前位置 主页 > 网站技术 > 代码类 >

    Python进阶之迭代器与迭代器切片教程

    栏目:代码类 时间:2020-01-30 06:07

    在前两篇关于 Python 切片的文章中,我们学习了切片的基础用法、高级用法、使用误区,以及自定义对象如何实现切片用法(相关链接见文末)。本文是切片系列的第三篇,主要内容是迭代器切片。

    迭代器是 Python 中独特的一种高级特性,而切片也是一种高级特性,两者相结合,会产生什么样的结果呢?

    1、迭代与迭代器

    首先,有几个基本概念要澄清:迭代、可迭代对象、迭代器。

    迭代 是一种遍历容器类型对象(例如字符串、列表、字典等等)的方式,例如,我们说迭代一个字符串“abc”,指的就是从左往右依次地、逐个地取出它的全部字符的过程。(PS:汉语中迭代一词有循环反复、层层递进的意思,但 Python 中此词要理解成单向水平线性 的,如果你不熟悉它,我建议直接将其理解为遍历。)

    那么,怎么写出迭代操作的指令呢?最通用的书写语法就是 for 循环。

    # for循环实现迭代过程
    for char in "abc":
      print(char, end=" ")
    # 输出结果:a b c

    for 循环可以实现迭代的过程,但是,并非所有对象都可以用于 for 循环,例如,上例中若将字符串“abc”换成任意整型数字,则会报错: 'int' object is not iterable .

    这句报错中的单词“iterable”指的是“可迭代的”,即 int 类型不是可迭代的。而字符串(string)类型是可迭代的,同样地,列表、元组、字典等类型,都是可迭代的。

    那怎么判断一个对象是否可迭代呢?为什么它们是可迭代的呢?怎么让一个对象可迭代呢?

    要使一个对象可迭代,就要实现可迭代协议,即要实现__iter__()魔术方法,换言之,只要实现了这个魔术方法的对象都是可迭代对象。

    那怎么判断一个对象是否实现了这个方法呢?除了上述的for循环外,我知道四种方法:

    # 方法1:dir()查看__iter__
    dir(2)   # 没有,略
    dir("abc") # 有,略
    
    # 方法2:isinstance()判断
    import collections
    isinstance(2, collections.Iterable)   # False
    isinstance("abc", collections.Iterable) # True
    
    # 方法3:hasattr()判断
    hasattr(2,"__iter__")   # False
    hasattr("abc","__iter__") # True
    
    # 方法4:用iter()查看是否报错
    iter(2)   # 报错:'int' object is not iterable
    iter("abc") # <str_iterator at 0x1e2396d8f28>
    
    ### PS:判断是否可迭代,还可以查看是否实现__getitem__,为方便描述,本文从略。

    这几种方法中最值得一提的是 iter() 方法,它是 Python 的内置方法,其作用是将可迭代对象变成迭代器 。这句话可以解析出两层意思:(1)可迭代对象跟迭代器是两种东西;(2)可迭代对象能变成迭代器。

    实际上,迭代器必然是可迭代对象,但可迭代对象不一定是迭代器。两者有多大的区别呢?

    如上图蓝圈所示,普通可迭代对象与迭代器的最关键区别可概括为:一同两不同 ,所谓“一同”,即两者都是可迭代的(__iter__),所谓“两不同”,即可迭代对象在转化为迭代器后,它会丢失一些属性(__getitem__),同时也增加一些属性(__next__)。

    首先看看增加的属性 __next__ , 它是迭代器之所以是迭代器的关键,事实上,我们正是把同时实现了 __iter__ 方法 和 __next__ 方法的对象定义为迭代器的。

    有了多出来的这个属性,可迭代对象不需要借助外部的 for 循环语法,就能实现自我的迭代/遍历过程。我发明了两个概念来描述这两种遍历过程(PS:为了易理解,这里称遍历,实际也可称为迭代):它遍历 指的是通过外部语法而实现的遍历,自遍历 指的是通过自身方法实现的遍历。