python中的迭代器和生成器(一)——迭代器

在學習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__方法返回一個迭代器。


參考資料:

  1. Python參考手冊
  2. Python基礎教程(第2版)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章