迭代器就是重复地做一些事情,可以简单的理解为循环,在python中实现了__iter__
方法的对象是可迭代的,实现了next()(在python3中使用__next__()
代替了next方法)方法的对象是迭代器,这样说起来有点拗口,实际上要想让一个迭代器工作,至少要实现__iter__
方法和next方法。很多时候使用迭代器完成的工作使用列表也可以完成,但是如果有很多值列表就会占用太多的内存,而且使用迭代器也让我们的程序更加通用、优雅、pythonic。
如果一个类想被用于for ... in
循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()
方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
不过迭代器是有限制的,例如
- 不能向后移动
- 不能回到开始
- 也无法复制一个迭代器。
因此要再次进行迭代只能重新生成一个新的迭代器对象。
获取迭代器
- 对于python内置的可迭代(iterable)对象,可以通过内置的
iter()
函数来获取相应的迭代器对象。
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相应的迭代器对象。
我们来看一下该迭代器对象的属性:
In [5]: dir(a)
Out[5]:
['__class__',
......
'__iter__',
'__le__',
......
'__next__',
'__reduce__',
......
'__str__',
'__subclasshook__']
In [6]:
可见此迭代对象具有两个特殊的成员方法__iter__()
和__next__()
,这两个方法便是支持迭代器协议所需要实现的方法。其中__iter__()
方法返回迭代器对象本身,__next__()
方法返回容器的下一个元素,直到结尾抛出StopIteration
异常。
我们来测试一下这个list_iterator
对象的这两个方法:
__iter__()
返回的对象就是迭代器对象本身。
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__()
方法返回容器中的值直到结尾。
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]:
- 创建迭代器对象
除了使用iter()
函数将内置的序列对象转换成相应的迭代器,我们可以自己实现迭代器协议创建迭代器对象,要实现迭代器协议也就是要在类中实现__iter__()
和__next__()
方法。
下面写一个与list_iterator
相同行为的迭代器:
# encoding:utf-8
class Fib(object):
def __init__(self, n):
self.gen = n
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > self.gen: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
if __name__ == '__main__':
for i in Fib(3):
print(i)
运行结果:
1
1
2
3
[Finished in 0.1s]
iter和next
- 内置函数iter()仅仅是调用了对象的
__iter()__
方法,所以list对象内部一定存在方法__iter__()
- 内置函数next()仅仅是调用了对象的
__next()__
方法,所以list对象内部不存在方法__next__()
,但是Itrator中一定存在这个方法。
>>> L = [4, 5, 6]
>>> '__iter__' in dir(L)
True
>>> '__next__' in dir(L)
False
>>> I = L.__iter__()
>>> '__next__' in dir(I)
True
>>> from collections import Iterable, Iterator
>>> isinstance(L, Iterable)
True
>>> isinstance(L, Iterator)
False
>>> isinstance(I, Iterable)
True
>>> isinstance(I, Iterator)
True
Iterator继承自Iterable,从下面的测试中可以很方便的看到Iterator包含__iter()__
和__next()__
方法,而Iteratble仅仅包含__iter__()
。
>>> from collections import Iterator, Iterable
>>> help(Iterator)
Help on class Iterator:
class Iterator(Iterable)
| Method resolution order:
| Iterator
| Iterable
| builtins.object
|**注解:从这里可以看出Iterable继承自object, Iterator继承自Iterable。
| Methods defined here:
|
| __iter__(self)
|
| __next__(self)
| Return the next item from the iterator. When exhausted, raise StopIteration
......
>>> help(Iterable)
Help on class Iterable:
class Iterable(builtins.object)
| Methods defined here:
|
| __iter__(self)
......
可迭代对象和迭代器对象
可迭代对象是实现了__iter__()
方法的对象,__iter__()
可以返回一个迭代器对象。
迭代器对象是实现了__next__()
方法的对象,其中他的__iter__()
返回的是迭代器对象本身。
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
上述例子中有着一个可迭代类和迭代器类
print('initialize a Iterable object......')
a = ListIterable([1, 2, 4, 5, 6])
print(type(a))
print('get Iterator object from __iter__ ')
b = a.__iter__()
print(type(b))
运行结果
initialize a Iterable object......
<class '__main__.ListIterable'>
get Iterator object from __iter__
call iterable __iter__().
<class '__main__.ListIterator'>
[Finished in 0.1s]
可见a是iterable
对象(实现了__iter__()
),b是iterator
对象(实现了__next__()
)。
下面看看这样做是不是就可以重复多次迭代了:
a = ListIterable([1, 2, 4, 5, 6])
print([i for i in a])
print([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__().
[1, 2, 4, 5, 6]
call iterable __iter__().
call iterator __next__().
call iterator __next__().
call iterator __next__().
call iterator __next__().
call iterator __next__().
call iterator __next__().
[1, 2, 4, 5, 6]
[Finished in 0.1s]
重复迭代是可以了,从输出中我们可以看出一些什么来
- 我们在使用迭代工具对
iterable
对象进行迭代的时候首先调用的是iterable
的__iter__()
方法,返回一个迭代器对象,也就是ListIterator
的实例。 - 然后再遍历的时候是调用
iterator
的next方法输出值。
这样就可以解释了为什么这样处理能够多次迭代了,因为每次使用迭代工具迭代的时候都会调用__iter__()
返回一个新的迭代器对象,这样就相当于创建多个迭代器了,自然可以看起来是重复迭代了!
可变对象和迭代器
在迭代可变对象时候,一个序列的迭代器只是记录当前到达了序列中的第几个元素,所以如果在迭代过程中改变了序列的元素。更新会立即反应到所迭代的条目上。
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
这就是边迭代边更改元素带来的危险了!
复制迭代器
迭代器是一次性消耗品,使用完了以后就空了,请看。
>>> L=[1,2,3]
>>> I=iter(L)
>>> for i in I:
... print(i, end='-')
...
1-2-3-
>>>next(I)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> I=iter(L)
>>> J=I
>>> next(I)
1
>>> next(J)
2
>>> next(I)
3
>>> next(J)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
说明迭代器的复制只是浅复制而已。