Python进阶之迭代器

Python 可迭代对象、迭代器的介绍

Posted by Surflyan on 2017-03-05

1.容器(container)

容器是存储元素的一种数据结构,可进行成员测试,在python中典型的容器有:

  • list
  • set
  • dict
  • tuple
  • str

容器较容易理解,可参照生活中容器的理解。

2. 可迭代对象(interable)

迭代(interate) 是指按照某种顺序逐个访问序列中的每一项。
大部分容器都是可迭代的,但还有其他对象也可迭代,如文件对象、管道对象,可迭代对象也可用来表示一个包含有限元素的数据结构。
可迭代对象可以是任意对象,只要该对象实现了 __iter__ 方法。
可用isinstance()判断一个对象是否是可迭代对象:

>>>from collections import Interable
>>>isinstance([],Iterable)
True
>>>isinstance('abc',Iterable)
True

__iter__ 方法会返回一个迭代器。那么什么是迭代器?

3. 迭代器(interator)

所谓的迭代器就是具有 next 方法的对象( 注:python3中为__next__() )。在调用 next 方法时,迭代器会返回它的下一个值,如果迭代器中没有值可以返回,会引发一个 StopIteration 异常。所以迭代器本质上是一个产生值的工厂,每次向迭代器请求下一个值,迭代器都会进行计算出相应的值并返回。

可用 isinstance()判断一个对象是否是迭代器:

>>>from collections import Iterator
>>>isinstance ((x for x in range(2)), Iterator)
True
>>>isinstance([],Iterator)
False

一般来说,迭代器本身也是可迭代的,即迭代器有返回自己的 next 方法。

>>>from collections import Iterator
>>>isinstance ((x for x in range(2)), Iterator)
True

listdictstr等虽然都是可迭代对象,却不是迭代器。但可以使用 iter()来使可迭代对象变成迭代器。

>>> x=[1,2,3]
>>> y=iter(x)
>>> type(x)
<type 'list'>
>>> type(y)
<type 'listiterator'>
>>> next(x)
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    next(x)
TypeError: list object is not an iterator
>>> next(y)
1
>>> dir(y)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__length_hint__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'next']

可以看出,x 是可迭代对象,y 是迭代器,在迭代器中,有 __iter__() 以及 next() 方法。
所有itertools模块中的函数都会返回一个迭代器。

综上: 可得出 生成迭代器的两种方法 :

  1. 为容器对象添加 __iter__()next() 方法(Python 3 中是 next());iter() 返回迭代器对象本身 self, next() 则返回每次调用 next()时或迭代时的元素;
  2. 内置函数 iter()将可迭代对象转化为迭代器

可自定义实现一个迭代器对象:

class MyRange(object):
    def __init__(self, n):
        print " I made this iterator!"
        self.i = 1
        self.n = n
    def __iter__(self):
        return self
    def next(self):
        print " Calling __next__ method!"
        if self.i <= self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()
if __name__ == "__main__":
    x = MyRange(6)
    for i in x:
        print i
`

运行结果是

 I made this iterator!
 Calling __next__ method!
1
 Calling __next__ method!
2
 Calling __next__ method!
3
 Calling __next__ method!
4
 Calling __next__ method!
5
 Calling __next__ method!
6
 Calling __next__ method!
>>>

看出来了吗?上面的代码其实就是自己实现的 range() 函数呢,而 for 语句其实时调用了迭代器的 next 函数。

4. 小结

  1. 在 Python 中,迭代器是遵循迭代协议的对象。
  2. 可以使用iter() 以从任何序列得到迭代器(如 list, tuple, dictionary, set 等)。
  3. 编写类,实现__iter__()方法,以及 next()(Python 2)或__next__()(Python 3) 。当没有元素时,则引发 StopIteration异常。
  4. 如果有很多值,列表就会占用太多的内存,而迭代器则占用更少内存,每次只记录一个值。
  5. 迭代器从第一个元素开始访问,直到所有的元素被访问完结束,只能往前不会后退。

请多多指教!