迭代器
迭代器是在python2.2中被加入的,它为类序列对象提供了一个类序列的接口。有了迭代器可以迭代一个不是序列的对象,因为他表现出了序列的行为。当在python中使用for循环迭代一个对象时,调用者几乎分辨不出他迭代的是一个迭代器对象还是一个序列对象,因为python让他(迭代器)像一个序列那样操作。
如何迭代
本质上说迭代器是个对象,但是这个对象有个特殊的方法next()
(在python3中使用__next__()
代替了next方法)。当使用for循环来遍历整个对象时候,就会自动调用此对象的__next__()
方法并获取下一个item。当所有的item全部取出后就会抛出一个StopIteration
异常,这并不是错误的发生,而是告诉外部调用者迭代完成了,外部的调用者尝试去捕获这个异常去做进一步的处理。
不过迭代器是有限制的,例如
- 不能向后移动
- 不能回到开始
- 也无法复制一个迭代器。
因此要再次进行迭代只能重新生成一个新的迭代器对象。
获取迭代器
- 对于python内置的可迭代(iterable)对象,可以通过内置的
iter()
函数来获取相应的迭代器对象。
1
2
3
4
5
6
7
8
9
|
In
[
1
]
:
a
=
[
1
,
2
,
3
,
45
]
In
[
2
]
:
type
(
a
)
Out
[
2
]
:
list
In
[
3
]
:
a
=
iter
(
a
)
In
[
4
]
:
type
(
a
)
Out
[
4
]
:
list_iterator
|
这样就获取了list相应的迭代器对象。
我们来看一下该迭代器对象的属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
In
[
5
]
:
dir
(
a
)
Out
[
5
]
:
[
'__class__'
,
'__delattr__'
,
'__dir__'
,
'__doc__'
,
'__eq__'
,
'__format__'
,
'__ge__'
,
'__getattribute__'
,
'__gt__'
,
'__hash__'
,
'__init__'
,
'__iter__'
,
'__le__'
,
'__length_hint__'
,
'__lt__'
,
'__ne__'
,
'__new__'
,
'__next__'
,
'__reduce__'
,
'__reduce_ex__'
,
'__repr__'
,
'__setattr__'
,
'__setstate__'
,
'__sizeof__'
,
'__str__'
,
'__subclasshook__'
]
In
[
6
]
:
|
可见此迭代对象具有两个特殊的成员方法__iter__()
和__next__()
,这两个方法便是支持迭代器协议所需要实现的方法。其中__iter__()
方法返回迭代器对象本身,__next__()
方法返回容器的下一个元素,直到结尾抛出StopIteration
异常。
我们来测试一下这个list_iterator
对象的这两个方法:
__iter__()
返回的对象就是迭代器对象本身。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
In
[
1
]
:
a
=
[
1
,
2
,
3
,
45
]
In
[
2
]
:
a
=
iter
(
a
)
In
[
3
]
:
a
.
__iter__
(
)
Out
[
3
]
:
<
list_iterator
at
0x3a33f10
>
In
[
4
]
:
a
Out
[
4
]
:
<
list_iterator
at
0x3a33f10
>
In
[
5
]
:
a
is
a
.
__iter__
(
)
Out
[
5
]
:
True
In
[
6
]
:
|
__next__()
方法返回容器中的值直到结尾。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
In
[
6
]
:
a
.
__next__
(
)
Out
[
6
]
:
1
In
[
7
]
:
a
.
__next__
(
)
Out
[
7
]
:
2
In
[
8
]
:
a
.
__next__
(
)
Out
[
8
]
:
3
In
[
9
]
:
a
.
__next__
(
)
Out
[
9
]
:
45
In
[
10
]
:
a
.
__next__
(
)
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
StopIteration
Traceback
(
most
recent
call
last
)
<
ipython
-
input
-
10
-
73aa2c76d676
>
in
<
module
>
(
)
--
--
>
1
a
.
__next__
(
)
StopIteration
:
In
[
11
]
:
|
2. 创建迭代器对象
除了使用iter()
函数将内置的序列对象转换成相应的迭代器,我们可以自己实现迭代器协议创建迭代器对象,要实现迭代器协议也就是要在类中实现__iter__()
和__next__()
方法。
下面我写一个与list_iterator
相同行为的迭代器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
ListIter
(
object
)
:
def
__init__
(
self
,
data
)
:
self
.
__data
=
data
self
.
__count
=
0
def
__iter__
(
self
)
:
return
self
def
__next__
(
self
)
:
if
self
.
__count
<
len
(
self
.
__data
)
:
val
=
self
.
__data
[
self
.
__count
]
self
.
__count
+=
1
return
val
else
:
raise
StopIteration
|
我们就可以使用for循环来遍历这个迭代器了:
1
2
3
4
5
6
7
8
9
10
11
12
|
In
[
16
]
:
a
=
ListIter
(
[
1
,
2
,
3
,
4
,
5
]
)
In
[
17
]
:
for
i
in
a
:
.
.
.
.
:
print
(
i
)
.
.
.
.
:
1
2
3
4
5
In
[
18
]
:
|
对于迭代器对象,使用for循环遍历整个数组其实是个语法糖,他的内部实现还是通过调用对象的__next__()
方法。
实际上他内部的工作原理应该是这样的:
1
2
3
4
5
6
7
8
9
|
a
=
ListIter
(
[
1
,
2
,
3
,
4
,
5
]
)
while
True
:
try
:
i
=
a
.
__next__
(
)
except
StopIteration
:
break
// do something in for loop
print
(
i
)
|
迭代器支持多次迭代
正如前面所说的迭代器对象不支持重新迭代,也就是同一个迭代器对象无法多次迭代,如:
1
2
3
4
5
6
7
8
9
|
In
[
19
]
:
a
=
ListIter
(
[
1
,
2
,
3
,
4
,
5
]
)
In
[
20
]
:
[
i
for
i
in
a
]
Out
[
20
]
:
[
1
,
2
,
3
,
4
,
5
]
In
[
21
]
:
[
i
for
i
in
a
]
Out
[
21
]
:
[
]
In
[
22
]
:
|
可见,当我再次迭代迭代器a的时候便只返回了空列表,这是因为for循环直接捕获了StopIteration
异常。如果要再次迭代生成列表的话只能重新生成一个新的迭代器对象。
为了能够解决这个问题,可以分别定义一个可迭代对象(iterables)和迭代器对象(iterator).
插入小插曲:
对于可迭代对象和迭代器对象,我的理解是:
可迭代对象是实现了__iter__()
方法的对象,__iter__()
可以返回一个迭代器对象。
迭代器对象是实现了__next__()
方法的对象,其中他的__iter__()
返回的是迭代器对象本身。
我把代码做了修改,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class
ListIterable
(
object
)
:
def
__init__
(
self
,
data
)
:
self
.
__data
=
data
def
__iter__
(
self
)
:
print
(
"call iterable __iter__()."
)
return
ListIterator
(
self
.
__data
)
class
ListIterator
(
object
)
:
def
__init__
(
self
,
data
)
:
self
.
__data
=
data
self
.
__count
=
0
def
__iter__
(
self
)
:
print
(
"call iterator __iter__()."
)
return
self
def
__next__
(
self
)
:
print
(
"call iterator __next__()."
)
if
self
.
__count
<
len
(
self
.
__data
)
:
val
=
self
.
__data
[
self
.
__count
]
self
.
__count
+=
1
return
val
else
:
raise
StopIteration
|
为了知道python何时调用__iter__()
方法,我添加了一个printf函数来做标记。
现在把这两个类导入到当前空间中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
In
[
1
]
:
from
list_iter
import
*
In
[
2
]
:
a
=
ListIterable
(
[
1
,
2
,
4
,
5
,
6
]
)
In
[
3
]
:
b
=
a
.
__iter__
(
)
call
iterables
__iter__
(
)
.
In
[
4
]
:
a
Out
[
4
]
:
<
list_iter
.
ListIterable
at
0x39446d0
>
In
[
5
]
:
b
Out
[
5
]
:
<
list_iter
.
ListIterator
at
0x39447b0
>
In
[
6
]
:
|
可见a是iterable
对象(实现了__iter__()
),b是iterator
对象(实现了__next__()
)。
下面看看这样做是不是就可以重复多次迭代了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
In
[
6
]
:
[
i
for
i
in
a
]
call
iterable
__iter__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
Out
[
6
]
:
[
1
,
2
,
4
,
5
,
6
]
In
[
7
]
:
[
i
for
i
in
a
]
call
iterable
__iter__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
call
iterator
__next__
(
)
.
Out
[
7
]
:
[
1
,
2
,
4
,
5
,
6
]
In
[
8
]
:
|
重复迭代是可以了,从输出中我们可以看出一些什么来
- 我们在使用迭代工具对
iterable
对象进行迭代的时候首先调用的是iterable
的__iter__()
方法,返回一个迭代器对象,也就是ListIterator
的实例。 - 然后再遍历的时候是调用
iterator
的next方法输出值。
这样就可以解释了为什么这样处理能够多次迭代了,因为每次使用迭代工具迭代的时候都会调用__iter__()
返回一个新的迭代器对象,这样就相当于创建多个迭代器了,自然可以看起来是重复迭代了!
可变对象和迭代器
在迭代可变对象时候,一个序列的迭代器只是记录当前到达了序列中的第几个元素,所以如果在迭代过程中改变了序列的元素。更新会立即反应到所迭代的条目上。
我写了个测试看了下,的确:
1
2
3
4
5
6
7
8
9
10
11
|
In
[
13
]
:
c
=
[
1
,
2
,
3
,
4
,
5
]
In
[
14
]
:
d
=
iter
(
c
)
In
[
15
]
:
for
i
in
c
:
.
.
.
.
:
print
(
i
)
.
.
.
.
:
c
.
remove
(
i
)
.
.
.
.
:
1
3
5
|
可见上面边迭代边删除列表的元素,但是最后却只输出了1, 3, 5
,这是为啥?
既然迭代器只记得是在列表中第几个元素,那么当在第0个元素的时候将会输出1然后删除1,这是列表变成了
1
|
[
2
,
3
,
4
,
5
]
|
但是迭代器记得我是在第二个位置上面,就指向了列表中的第二个位置上,也就是3,然后输出3.
以此类推,最后只能输出1,3,5了。
如果我猜测的没错的话,剩余的列表应该只剩下2和4了:
1
2
|
In
[
17
]
:
c
Out
[
17
]
:
[
2
,
4
]
|
果然!
以上就是我对python中迭代器方面的一点小总结。