在學習python的資料中,經常看到迭代器和生成器,這樣的名詞。但到底什麼是迭代器,什麼是生成器?爲什麼會出現這樣的類型,他們有什麼便捷之處?
什麼是迭代?
簡單的講就是循環操作,重複某一個過程很多次。可以用for循環對可迭代對象進行迭代。
什麼是可迭代對象?
常見的迭代對象有序列,字典和文件等,另外還有其他的對象。這些可迭代對象的特點是:實現了__iter__方法。例如:
>>> alist=[1,2,3]
>>> lst=alist.__iter__()
>>> type(lst)
<type 'listiterator'>
>>> atuple=(1,2,3)
>>> tup=atuple.__iter__()
>>> type(tup)
<type 'tupleiterator'>
>>> afile=open(r"e:\test.txt",'r')
>>> fl=afile.__iter__()
>>> type(fl)
<type 'file'>
>>> astr='abc'
>>> string=astr.__iter__()
Traceback (most recent call last):
File "<pyshell#62>", line 1, in <module>
string=astr.__iter__()
AttributeError: 'str' object has no attribute '__iter__'
>>> iter(astr)
<iterator object at 0x0212BE70>
>>> string=iter(astr)
>>> anum=1
>>> iter(anum)
Traceback (most recent call last):
File "<pyshell#66>", line 1, in <module>
iter(anum)
TypeError: 'int' object is not iterable
>>> type(string)
<type 'iterator'>
>>>
我們注意到,__iter__方法返回的對象就是迭代器對象。對於字符串astr來說,沒有__iter__方法,但是可以用內建函數iter()來調用,所以是可迭代對象。而對於數字anum來說,iter()無法調用,所以是不可迭代的對象。因此可以用iter()來判斷一個對象是否是可迭代對象。
內建函數iter()和方法__iter__的關係:
>>> help(list.__iter__)
Help on wrapper_descriptor:
__iter__(...)
x.__iter__() <==> iter(x)
>>>
什麼是迭代器?
迭代器就是有一個 next() 方法的對象。
在Python3.0中迭代器規則有些變化,迭代器對象應該實現__next__方法,而不是next。而新的內建函數next可以用於訪問這個方法。換句話說,next(it)等同於3.0之前版本中的it.next()。
看看上面的迭代器是否都有next():
>>> lst.next()
1
>>> tup.next()
1
>>> fl.next()
'\xef\xbb\xbfHello world!\n'
>>> string.next()
'a'
如何迭代?
在後臺,for 語句在容器對象中調用iter().該函數返回一個定義了next()方法的迭代器對象,它在容器中逐一訪問元素。沒有後續元素時,next()就會拋出一個 StopIteration 異常, 這並不表示錯誤發生, 只是通知外部調用者,for語句循環結束, 迭代完成。
以下是其工作原理的示例:
>>> s='abc'
>>> it=iter(s)
>>> it
<iterator object at 0x0228F9D0>
>>> it.next()
'a'
>>> it.next()
'b'
>>> it.next()
'c'
>>> it.next()
Traceback (most recent call last):
File "<pyshell#33>", line 1, in <module>
it.next()
StopIteration
>>>
如何自定義迭代器?
瞭解了迭代器協議的後臺機制,就可以很容易的給自己的類添加迭代器行爲。定義一個 __iter__() 方法,使其返回一個帶有 next() 方法的對象。如果這個類已經定義了 next(),那麼 __iter__() 只需要返回self():
>>> class Reversestr:
'Iterator for looping over a swquence backwards'
def __init__(self,data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
>>> for char in Reversestr('spam'):
print char
m
a
p
s
>>>
迭代器的優點是什麼?列表不可以嗎?
如果有一個可迭代計算值的函數,那麼在使用時可能只需要獲得指定的值,而不需要獲得所有值的列表。這時候,如果數值很多,使用列表會佔用太多的內存。當然,使用迭代器更簡單更優雅。
看一個不使用列表的例子,因爲那樣列表的長度必須無限。
>>> class Fibs():
def __init__(self):
self.a=0
self.b=1
def next(self):
self.a,self.b = self.b,self.a + self.b
return self.a
def __iter__(self):
return self
>>>
構造一個Fibs對象:
>>> fibs = Fibs()
在for循環中使用該對象——比如查找在斐波那契數列中比500大的最小的數:
>>> for f in fibs:
if f > 500:
print f
break
610
因爲設置了break,所以循環停止了,否則會一直繼續下去。
迭代器和可迭代對象之間的關係
內建函數iter可以從可迭代對象中獲取迭代器,以list爲例:
>>> a=[1,2,3]
>>> type(a)
<type 'list'>
>>> it=iter(a)
>>> type(it)
<type 'listiterator'>
>>> it.next()
1
>>> it.next()
2
>>> it.next()
3
>>> it.next()
Traceback (most recent call last):
File "<pyshell#140>", line 1, in <module>
it.next()
StopIteration
>>>
注:iter(a)和a.__iter__()等價。
反過來,使用list構造方法可以將迭代器轉化爲列表:
>>> a=[1,2,3]
>>> it=iter(a)
>>> list(it)
[1, 2, 3]
>>>
總結:
可迭代對象:一個實現了__iter__方法的對象。
迭代器:一個實現了next方法的對象。
關係:__iter__方法返回一個迭代器。
參考資料:
- Python參考手冊
- Python基礎教程(第2版)