迭代器這個(gè)概念在很多語(yǔ)言中(比如 C++,Java)都是存在的,但是不同語(yǔ)言實(shí)現(xiàn)迭代器的方式各不相同。在 Python 中,迭代器是指遵循迭代器協(xié)議(iterator protocol)的對(duì)象。至于什么是迭代器協(xié)議,稍后自然會(huì)說明。為了更好地理解迭代器,我先介紹和迭代器相關(guān)的兩個(gè)概念:
你可能會(huì)覺得這是在玩文字游戲,但這確實(shí)是要搞清楚的。
當(dāng)我們用一個(gè)循環(huán)(比如 for 循環(huán))來(lái)遍歷容器(比如列表,元組)中的元素時(shí),這種遍歷的過程就叫迭代。
在 Python 中,我們使用 for...in... 進(jìn)行迭代。比如,遍歷一個(gè) list:
numbers = [1, 2, 3, 4]
for num in numbers:
print num
像上面這種可以使用 for 循環(huán)進(jìn)行迭代的對(duì)象,就是可迭代對(duì)象,它的定義如下:
含有
__iter__()方法或__getitem__()方法的對(duì)象稱之為可迭代對(duì)象。
我們可以使用 Python 內(nèi)置的 hasattr() 函數(shù)來(lái)判斷一個(gè)對(duì)象是不是可迭代的:
>>> hasattr((), '__iter__')
True
>>> hasattr([], '__iter__')
True
>>> hasattr({}, '__iter__')
True
>>> hasattr(123, '__iter__')
False
>>> hasattr('abc', '__iter__')
False
>>> hasattr('abc', '__getitem__')
True
另外,我們也可使用 isinstance() 進(jìn)行判斷:
>>> from collections import Iterable
>>> isinstance((), Iterable) # 元組
True
>>> isinstance([], Iterable) # 列表
True
>>> isinstance({}, Iterable) # 字典
True
>>> isinstance('abc', Iterable) # 字符串
True
>>> isinstance(100, Iterable) # 數(shù)字
False
可見,我們熟知的字典(dict)、元組(tuple)、集合(set)和字符串對(duì)象都是可迭代的。
現(xiàn)在,讓我們看看什么是迭代器(Iterator)。上文說過,迭代器是指遵循迭代器協(xié)議(iterator protocol)的對(duì)象。從這句話我們可以知道,迭代器是一個(gè)對(duì)象,但比較特別,它需要遵循迭代器協(xié)議,那什么是迭代器協(xié)議呢?
迭代器協(xié)議(iterator protocol)是指要實(shí)現(xiàn)對(duì)象的
__iter()__和next()方法(注意:Python3 要實(shí)現(xiàn)__next__()方法),其中,__iter()__方法返回迭代器對(duì)象本身,next()方法返回容器的下一個(gè)元素,在沒有后續(xù)元素時(shí)拋出StopIteration異常。
接下來(lái)講講迭代器的例子,有什么常見的迭代器呢?列表是迭代器嗎?字典是迭代器嗎?我們使用 hasattr() 進(jìn)行判斷:
>>> hasattr((1, 2, 3), '__iter__')
True
>>> hasattr((1, 2, 3), 'next') # 有 __iter__ 方法但是沒有 next 方法,不是迭代器
False
>>>
>>> hasattr([1, 2, 3], '__iter__')
True
>>> hasattr([1, 2, 3], 'next')
False
>>>
>>> hasattr({'a': 1, 'b': 2}, '__iter__')
True
>>> hasattr({'a': 1, 'b': 2}, 'next')
False
同樣,我們也可以使用 isinstance() 進(jìn)行判斷:
>>> from collections import Iterator
>>>
>>> isinstance((), Iterator)
False
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('', Iterator)
False
>>> isinstance(123, Iterator)
False
可見,雖然元組、列表和字典等對(duì)象是可迭代的,但它們卻不是迭代器!對(duì)于這些可迭代對(duì)象,可以使用 Python 內(nèi)置的 iter() 函數(shù)獲得它們的迭代器對(duì)象,看下面的使用:
>>> from collections import Iterator
>>> isinstance(iter([1, 2, 3]), Iterator) # 使用 iter() 函數(shù),獲得迭代器對(duì)象
True
>>> isinstance(iter('abc'), Iterator)
True
>>>
>>> my_str = 'abc'
>>> next(my_str) # my_str 不是迭代器,不能使用 next(),因此出錯(cuò)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-5f369cd8082f> in <module>()
----> 1 next(my_str)
TypeError: str object is not an iterator
>>>
>>> my_iter = iter(my_str) # 獲得迭代器對(duì)象
>>> isinstance(my_iter, Iterator)
True
>>> next(my_iter) # 可使用內(nèi)置的 next() 函數(shù)獲得下一個(gè)元素
'a'
事實(shí)上,Python 的 for 循環(huán)就是先通過內(nèi)置函數(shù) iter() 獲得一個(gè)迭代器,然后再不斷調(diào)用 next() 函數(shù)實(shí)現(xiàn)的,比如:
for x in [1, 2, 3]:
print i
等價(jià)于
# 獲得 Iterator 對(duì)象
it = iter([1, 2, 3])
# 循環(huán)
while True:
try:
# 獲得下一個(gè)值
x = next(it)
print x
except StopIteration:
# 沒有后續(xù)元素,退出循環(huán)
break
現(xiàn)在,讓我們來(lái)自定義一個(gè)迭代器:斐波那契(Fibonacci)數(shù)列迭代器。根據(jù)迭代器的定義,我們需要實(shí)現(xiàn) __iter()__ 和 next() 方法(在 Python3 中是 __next__() 方法)。先看代碼:
# -*- coding: utf-8 -*-
from collections import Iterator
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
# 返回迭代器對(duì)象本身
def __iter__(self):
return self
# 返回容器下一個(gè)元素
def next(self):
self.a, self.b = self.b, self.a + self.b
return self.a
def main():
fib = Fib() # fib 是一個(gè)迭代器
print 'isinstance(fib, Iterator): ', isinstance(fib, Iterator)
for i in fib:
if i > 10:
break
print i
if __name__ == '__main__':
main()
在上面的代碼中,我們定義了一個(gè) Fib 類,用于生成 Fibonacci 數(shù)列。在類的實(shí)現(xiàn)中,我們定義了 __iter__ 方法,它返回對(duì)象本身,這個(gè)方法會(huì)在遍歷時(shí)被 Python 內(nèi)置的 iter() 函數(shù)調(diào)用,返回一個(gè)迭代器。類中的 next() 方法用于返回容器的下一個(gè)元素,當(dāng)使用 for 循環(huán)進(jìn)行遍歷的時(shí)候,就會(huì)使用 Python 內(nèi)置的 next() 函數(shù)調(diào)用對(duì)象的 next 方法(在 Python3 中是 __next__ 方法)對(duì)迭代器進(jìn)行遍歷。
運(yùn)行上面的代碼,可得到如下結(jié)果:
isinstance(fib, Iterator): True
1
1
2
3
5
8
iter() 函數(shù)獲得一個(gè)迭代器對(duì)象;for 循環(huán)實(shí)質(zhì)上是先通過內(nèi)置函數(shù) iter() 獲得一個(gè)迭代器,然后再不斷調(diào)用 next() 函數(shù)實(shí)現(xiàn)的;__iter()__ 和 next() 方法(注意:Python3 要實(shí)現(xiàn) __next__() 方法),其中,__iter()__ 方法返回迭代器對(duì)象本身,next() 方法返回容器的下一個(gè)元素,在沒有后續(xù)元素時(shí)拋出 StopIteration 異常。