本文主要介绍Python迭代器的相关知识。

在此之前,先用一幅图,了解下容器(container)、可迭代对象(iterable)、迭代器(iterator)、生成器(generator)、列表/集合/字典推导式(list,set,dict comprehension)等概念的一些关系(本图片来源于互联网,由于画的很好,这里借鉴过来)。

容器(container)

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in,not in关键字判断元素是否包含在容器中。通常这类数据结构把所有的元素存储在内存中(也有一些特例,并不是所有的元素都放在内存,比如迭代器和生成器对象)在Python中,常见的容器对象有:

1
2
3
4
5
list, deque, ....
set, frozensets, ....
dict, defaultdict, OrderedDict, Counter, ....
tuple, namedtuple, ....
str

容器比较容易理解,因为你就可以把它看作是一个盒子、一栋房子、一个柜子,里面可以塞任何东西。从技术角度来说,当它可以用来询问某个元素是否包含在其中时,那么这个对象就可以认为是一个容器。

可迭代对象(iterable)

可直接作用域for循环的类型,主要包括以下几类:

1.集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
2.generator ,包括生成器和带 yield 的generator function。

这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable 。

判断是否可以迭代

可以使用 isinstance() 判断一个对象是否是 Iterable 对象:

1
2
3
4
5
6
from collections import Iterable
isinstance([], Iterable) # True
isinstance({}, Iterable) # True
isinstance('abc', Iterable) # True
isinstance((x for x in range(10)), Iterable) # True
isinstance(100, Iterable) # False

而生成器不但可以作用于 for 循环,还可以被 next() 函数不断调用并返回下一个值,直到最后抛出 StopIteration 错误表示无法继续返回下一个值了。

迭代器(Iterator)

它是一个带状态的对象,他能在你调用next()方法的时候返回容器中的下一个值。如果容器中没有更多元素了,则抛出StopIteration异常。

所以,迭代器就是实现了工厂模式的对象,它在你每次你询问要下一个值的时候给你返回。

比如itertools函数返回的都是迭代器对象。

生成无限序列:

1
2
3
4
5
6
>>> from itertools import count
>>> counter = count(start=13)
>>> next(counter)
13
>>> next(counter)
14

从一个有限序列中生成无限序列:

1
2
3
4
5
6
7
8
9
10
>>> from itertools import cycle
>>> colors = cycle(['red', 'white', 'blue'])
>>> next(colors)
'red'
>>> next(colors)
'white'
>>> next(colors)
'blue'
>>> next(colors)
'red'

从无限的序列中生成有限序列:

1
2
3
4
5
6
7
8
9
>>> from itertools import islice
>>> colors = cycle(['red', 'white', 'blue']) # infinite
>>> limited = islice(colors, 0, 4) # finite
>>> for x in limited:
... print(x)
red
white
blue
red

为了更直观地感受迭代器内部的执行过程,我们自定义一个迭代器,以斐波那契数列为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Fib:
def __init__(self):
self.prev = 0
self.curr = 1

def __iter__(self):
return self

def __next__(self):
value = self.curr
self.curr += self.prev
self.prev = value
return value

>>> f = Fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

实例变量prev和curr用户维护迭代器内部的状态。每次调用next()方法的时候做两件事:

1.为下一次调用next()方法修改状态
2.为当前这次调用生成返回结果

迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。

判断Iterator对象

可以使用 isinstance()判断一个对象是否是Iterator对象:

1
2
3
4
5
6
from collections import Iterator
isinstance((x for x in range(10)), Iterator) # True
isinstance([], Iterator) # False
isinstance({}, Iterator) # False
isinstance('abc', Iterator) # False
isinstance(100, Iterator) # False

iter()函数

生成器都是Iterator对象,但 list、dict、str 虽然是 Iterable,却不是Iterator。

把 list、dict 、str等Iterable变成Iterator可以使用iter() 函数:

1
2
isinstance(iter([]), Iterator)  # True
isinstance(iter('abc'), Iterator) # True

参考资料: