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
3for 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
2from collections import Iterable
isinstance(obj,Iterable)__iter__()
方法(直到python3.9)来确定其返回值.
但这个判断方法并不符合python对iterable的定义.也就是说并不是所有的iterable都具有__iter__()
方法.
所以我不推荐这个方法. 后面会讲到这种方法无法判断的例外.
想要确定一个对象是否是iterable, 即可以通过for语句遍历(即数学上可数的), 可以用如下函数:
1 | def isIterable(x): |
可见可迭代对象等定义在执行中等价于可以传递给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被叫做序列:
- 能够通过整数索引来做成员访问.
即具有方法
__getitem__()
并其参数可以是整数. - 能够获得长度, 即具有方法
__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 functionnext()
) 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.
迭代器是一种数据流的抽象. 迭代器的具体实现也是有两个行为:
- 实现
__next__()
方法, 这个方法返回的是数据流中的下一个成员. 当所有成员都遍历过了, 再次调用方法__next__()
, 必须抛出StopIteration
异常代表迭代器耗尽. python built-in函数next(obj)
等效于调用obj.__next__()
. - 实现
__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对象.
如果没有第二个参数, 只有第一个参数
- 如果对象满足迭代协议,
obj.__iter__()
将被调用, 其返回值作为iter(obj)
的返回值. - 如果对象满足序列协议,
Python会自动通过
__getitem__
和__len__
方法自动产生一个迭代器, 这种情况大致上iter(obj)
等价于如下代码:
1 | def iter(obj): |
- 如果上面两条协议都不满足, 那么将抛出异常TypeError.
如果给了第二个参数sentinel,
第一个参数必须是一个可调用对象(callable object).
iter()
语义变成"通过对可调用对象的调用返回一系列值,
直到卫兵(sentinel)被调用的对象返回. iter(obj,
sentinel)大致上等价于如下代码:
1 | def iter(obj, sentinel): |
生成器 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 | def generator_fun(): |
转化
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 | 序列(队列) |