python iterable(可迭代对象) vs iterator(迭代器) vs sequence(序列) vs generator(生成器)

这四个概念是Python为了解决容器对象遍历问题抽象出来的概念. 三者之间的关系是:

\[ \text{generator}\subset\text{iterator}\subset\text{iterable}. \] \[ \text{sequence}\subset\text{iterable}. \]

可迭代对象 iterable

Python官方文档的定义如下:

An object capable of returning its members one at a time.

定义非常抽象, 中文翻译的也算精准, 凡是能够一次返回一个成员的容器对象都是可迭代对象. 理解上可以把可迭代对象对应数学上的可数集.(容器对象对应数学上的集合)

可数集分为有限可数集和无限可数集, iterable也分两类: sequence和iterator. 其中序列(sequence)对应数学里的有限可数集, 迭代器(iterator)可以类比于无限可数集(其实也是有限的, 只不过长度未知).

后面再分别细说这两种对象, 先说一下可迭代对象的用处. 最常用的是用在for语句里(所以有些文章会把iterable定义为可以用在for语句in后面的对象). 当然还有很多其他语句里可以使用iterable. 比如built-in函数zip(),map()等, 或者推导式(comprehension). 但本质上还是for语句的变体. 这里解释一下for语句在python里的本质.

下面的for语句:

1
2
3
for member in iterable_obj:
pass
# statement_block

实际上python是按照如下进行实现的:

1
2
3
4
5
6
7
8
9
10
11
# 首先获得Iterator对象:
iterator_obj = iter(iterable_obj)
# 循环:
while True:
try:
# 获得下一个值:
member = next(iterator_obj)
# statement_block
except StopIteration:
# 遇到StopIteration就退出循环
break

这里built-in函数iter()next()后面再详细解释.

判断一个对象是否是iterable, python的collections可以通过下面的方法.

1
2
from collections import Iterable
isinstance(obj,Iterable)
这个方法的实现是通过判断对象是否具有__iter__()方法(直到python3.9)来确定其返回值. 但这个判断方法并不符合python对iterable的定义.也就是说并不是所有的iterable都具有__iter__()方法. 所以我不推荐这个方法. 后面会讲到这种方法无法判断的例外.

想要确定一个对象是否是iterable, 即可以通过for语句遍历(即数学上可数的), 可以用如下函数:

1
2
3
4
5
6
def isIterable(x):
try:
iter(x)
return True
except:
return False

可见可迭代对象等定义在执行中等价于可以传递给built-in函数iter()并且不抛出异常的对象. 这里dd额buit-in函数iter()和特殊方法__iter__()将在后面介绍.

序列 sequence

Python官方文档的定义如下:

An iterable which supports efficient element access using integer indices via the __getitem__() special method and defines a __len__() method that returns the length of the sequence.

即能够做下面两种行为的iterable被叫做序列:

  1. 能够通过整数索引来做成员访问. 即具有方法__getitem__()并其参数可以是整数.
  2. 能够获得长度, 即具有方法__len__(). 这也是为什么前面说sequence可以类比于有限可数集. 因为其具有长度.

这两个特殊方法加上整数索引合起来叫做序列协议(sequence protocol). 实现了序列协议的iterable对象就可以叫做序列. 比如python built-in sequence类型list, str, tuple and bytes. 注意dict虽然也有方法__getitem__()__len__(), 但被认为是mapping而不是sequence, 因为不满足上面说的通过整数索引. 但是dict是可迭代对象(iterable), 因为其可以逐一返回其容器内对象.

迭代器 iterator

Python官方文档的定义如下:

An object representing a stream of data. Repeated calls to the iterator's __next__() method (or passing it to the built-in function next()) return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its __next__() method just raise StopIteration again. Iterators are required to have an __iter__() method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted.

迭代器是一种数据流的抽象. 迭代器的具体实现也是有两个行为:

  1. 实现__next__()方法, 这个方法返回的是数据流中的下一个成员. 当所有成员都遍历过了, 再次调用方法__next__(), 必须抛出StopIteration异常代表迭代器耗尽. python built-in函数next(obj)等效于调用obj.__next__().
  2. 实现__iter__()方法返回一个迭代器对象. 通常返回迭代器对象自身.

这两个方法合起来叫做迭代器协议(iterator protocol), 也就是说只要实现了这个协议的对象都成为迭代器. 可见满足迭代器协议的对象一定也满足迭代协议.

讲到这里就可以回到文章刚开始提起for循环的本质

1
2
3
4
5
6
7
8
9
10
11
# 首先获得Iterator对象:
iterator_obj = iter(iterable_obj)
# 循环:
while True:
try:
# 获得下一个值:
member = next(iterator_obj)
# statement_block
except StopIteration:
# 遇到StopIteration就退出循环
break

里面的built-in函数next()已经知道是什么意思了. 现在说一下iter().

Python官方文档对这个内置函数描述如下:

iter(object[, sentinel]) Return an iterator object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, object must be a collection object which supports the iteration protocol (the __iter__() method), or it must support the sequence protocol (the __getitem__() method with integer arguments starting at 0). If it does not support either of those protocols, TypeError is raised. If the second argument, sentinel, is given, then object must be a callable object. The iterator created in this case will call object with no arguments for each call to its __next__() method; if the value returned is equal to sentinel, StopIteration will be raised, otherwise the value will be returned.

即这个函数会返回一个iterator对象.

如果没有第二个参数, 只有第一个参数

  1. 如果对象满足迭代协议, obj.__iter__()将被调用, 其返回值作为iter(obj)的返回值.
  2. 如果对象满足序列协议, Python会自动通过__getitem____len__方法自动产生一个迭代器, 这种情况大致上iter(obj)等价于如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def iter(obj):
class sequence_iter:
def __init__(self, obj):
self.i = 0
self.obj = obj
def __iter__(self):
return self
def __next__(self):
self.i += 1
if self.i <= len(self.obj):
return self.obj[self.i - 1]
else:
raise StopIteration
return sequence_iter(obj)
  1. 如果上面两条协议都不满足, 那么将抛出异常TypeError.

如果给了第二个参数sentinel, 第一个参数必须是一个可调用对象(callable object). iter()语义变成"通过对可调用对象的调用返回一系列值, 直到卫兵(sentinel)被调用的对象返回. iter(obj, sentinel)大致上等价于如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
def iter(obj, sentinel):
class sentinel_iter:
def __init__(self, obj):
self.obj = obj
def __iter__(self):
return self
def __next__(self):
v = self.obj()
if v == sentinel:
raise StopIteration
else:
return v
return sentinel_iter(obj)

生成器 generator

Python官方文档的定义如下:

A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.

Usually refers to a generator function, but may refer to a generator iterator in some contexts. In cases where the intended meaning isn’t clear, using the full terms avoids ambiguity.

这里generator在使用上是有歧义的, 既可能指生成器函数(generator function), 也可能指生成器迭代器(generator iterator). 本文里用generator指后者, 前者直接用生成器函数.

所以生成器是一种特殊迭代器, 由生成器函数产生的迭代器. 因而\(\text{generator}\subset\text{iterator}\). 生成器函数跟普通函数只有一点不一样, 生成器函数含有yield表达式. 反复调用生成器函数可以产生一系列的值. 调用生成器函数, 函数体不会执行, 而是返回一个生成器. 然后当我满按照迭代器的使用, 遍历其成员的时候, 生成器的__next__()方法通过执行生成器函数的函数体来产生一系列值.

下面的代码是一个generator, 可见其符合迭代器协议.

1
2
3
4
5
6
7
8
9
10
11
12
13
def generator_fun():
i = 0
while True:
yield i
if i >= 5:
return
i += 1

generator = generator_fun()
print(hasattr(generator, '__iter__')) #True
print(hasattr(generator, '__next__')) #True
for i in generator:
print(i)

转化

iterator转化为sequence可以通过类似于list(obj)或者推导式(comprehension)来实现. sequence转为为iterator可以通过iter(obj)来实现.

总结

  • iterable是可以放在for语句里的对象.也可以定义为可以传递给iter()的对象.
  • iterable可以通过iter(obj)转化为iterator
  • iterable包含两类iterator和sequence
  • sequence要符合sequence protocol, 即含有__getitem__()__len__()方法, 可整数索引.
  • iterator要符合iterator protocol, 即含有__next__()__iter__()方法
  • generator是一种特殊的iterator, 调用含有yield语句的generator function(生成器函数), 将返回一个generator.

专有名词翻译

英文 中文
comprehension 推导式
generator 生成器
generator function 生成器函数
generator iterator 生成器迭代器
iterable 可迭代对象(可遍历对象)
iterator 迭代器
sequence 序列(队列)