一.什麼玩意是迭代器?
先說說什麼是迭代吧,迭代就是一件事情重複很多次,比如說for迴圈。
for迴圈可以對一切有iter方法的對象進行迭代,那麼什麼是iter方法呢?
一個對象是否可迭代,全都取決於這個對象是否有iter方法,調用對象的iter方法,就回返回一個迭代器,這個迭代器一定具有next方法,在調用這個迭代器的next方法時,迭代器就回返回它的下一個值,當迭代器中沒有值可以返回了,就回拋出一個名為StopIteration的異常,停止迭代。
迭代器還有個很重要的特性,就是無法復原,只能前進,不能後退。
for迴圈就是這樣工作的,for迴圈在迴圈一個對象的時候,會調用這個對象的iter方法,得到迭代器,然後在調用這個迭代器的next方法,去獲得這個迭代器中包涵的每個值。
二.列表和迭代器的區別在哪裡?如何可以實現一個基本的迭代器?
迭代器的工作方式,是在使用的時候計算一個值擷取一個值,而列表呢,是一次性擷取所有的值,如果有很多值,就會佔用很大的記憶體。
當自己建立一個對象時,如何讓自己的對象可迭代?
class test_class:
def init(self,start_num,stop_num):
self.start_num = start_num
self.stop_num = stop_num
def next(self):
if self.start_num < self.stop_num:
self.start_num += 1
return self.start_num
def iter(self):
return self
test_obj = test_class(0,3)
print test_obj.next()
>>>1
print test_obj.next()
>>>2
print test_obj.next()
>>>3
三.什麼是產生器?
個人的理解,產生器是個比較特殊的可迭代對象,它與其他的可迭代對象不太一樣的地方,就是,其他的可迭代對象需要調用iter方法,返回個迭代器對象,然後通過迭代器對象去執行next方法,擷取迭代器中的值,但是產生器直接可以被迭代,無需執行iter方法。
在python中產生器有兩種表達形式:
函數式產生器:也就是字面意思,在常規的函數中定義的產生器,語句的傳回值不再使用return去返回,而是使用yield關鍵字每次返回一個結果,一個函數中不可以有多個return,但是可以有多個yield,函數中的每一個yield都會返回一個結果,每執行一個yield,函數的執行狀態都會被‘掛起’可以理解為暫停,下次繼續調用這個函數的時候,會從上次掛起的位置繼續向下執行。
下面是關於函數式產生器的例子:
下面這個例子驗證了yield的兩種特性,第一種是一個函數可以yield多個值,有多個yield,另外一個就是函數式產生器的掛起特性。
def func1():
yield 1
print "第一個yield執行完成~"
yield 2
print "第二個yield執行完成~"
yield 3
print "第三個yield執行完成~"
for i in func1():
print i
>>>1
第一個yield執行完成~
2
第二個yield執行完成~
3
第三個yield執行完成~
產生器運算式:使用類似於列表推導式的方法,但是返回的對象不再是一個列表,而是一個可以按需產生結果的一個對象(產生器)。
例1:
for i in (i for i in range(10000)):
print i
(i for i in range(5)) 這個就是產生器運算式。
(i for i in range(10000)) = def test(): for i in range(10000):yield i
這兩個種寫法起到的作用是一樣的,只不過是寫法不同,一個是產生器運算式,另一種是函數式產生器。
有沒有覺得這種產生器運算式和列表推導式看起來很像,不同的地方就在於列表推導式是使用[]中括弧,而產生器運算式使用的是()小括弧?
事實就是如此,它們之間的文法確實只差一個括弧,但是,產生器運算式更節省記憶體空間。
關於產生器,大致就說的差不多了,最後來個總結:
產生器的定義方法與普通的函數是一模一樣的,不同的地方就是產生器使用yield返回一個值,函數使用return返回一個值。
在python中,產生器會自動實現迭代協議,在沒有值可以返回的時候,返回一個StopIteration異常。
產生器使用yield語句返回一個值。yield語句掛起該產生器函數的狀態,保留足夠的資訊,以便之後從它離開的地方繼續執行。
下面的例子是列表推導式和產生器運算式執行的效率對比,感興趣的小夥伴可以在自己電腦上執行一下試試。
#列表解析
sum([i for i in range(100000000)])#記憶體佔用大,機器容易卡死
#產生器運算式
sum(i for i in range(100000000))#幾乎不佔記憶體