迭代器(iterator)
def add(s, x): return s + xdef gen(): for i in range(4): yield ibase = gen()for n in [1, 10]: base = (add(i, n) for i in base)print list(base)
這個東西輸出可以腦補一下, 結果是[20,21,22,23], 而不是[10, 11, 12, 13]。
要說產生器,必須首先說迭代器
區分iterable,iterator與itertion
講到迭代器,就需要區別幾個概念:iterable,iterator,itertion, 看著都差不多,其實不然。下面區分一下。
itertion: 就是迭代,一個接一個(one after another),是一個通用的概念,比如一個迴圈遍曆某個數組。
iterable: 這個是可迭代對象,屬於python的名詞,範圍也很廣,可重複迭代,滿足如下其中之一的都是iterable: 可以for迴圈: for i in iterable 可以按index索引的對象,也就是定義了getitem方法,比如list,str; 定義了iter方法。可以隨意返回。 可以調用iter(obj)的對象,並且返回一個iterator
iterator: 迭代器對象,也屬於python的名詞,只能迭代一次。需要滿足如下的迭代器協議 定義了iter方法,但是必須返回自身 定義了next方法,在python3.x是next。用來返回下一個值,並且當沒有資料了,拋出StopIteration可以保持當前的狀態
首先str和list是iterable 但不是iterator:
In [13]: si = iter(s)In [14]: siOut[14]: <iterator at 0x7f9453279dd0>In [15]: si.__iter__ # 有__iter__Out[15]: <method-wrapper '__iter__' of iterator object at 0x7f9453279dd0>In [16]: si.next #擁有nextOut[16]: <method-wrapper 'next' of iterator object at 0x7f9453279dd0>In [20]: si.__iter__() is si #__iter__返回自己Out[20]: TrueIn [1]: %pasteclass DataIter(object): def __init__(self, *args): self.data = list(args) self.ind = 0 def __iter__(self): #返回自身 return self def next(self): # 返回資料 if self.ind == len(self.data): raise StopIteration else: data = self.data[self.ind] self.ind += 1 return data## -- End pasted text --In [9]: d = DataIter(1,2)In [10]: for x in d: # 開始迭代 ....: print x ....:12In [13]: d.next() # 只能迭代一次,再次使用則會拋異常---------------------------------------------------------------------------StopIteration Traceback (most recent call last)----> 1 d.next()<ipython-input-1-c44abc1904d8> in next(self) 10 def next(self): 11 if self.ind == len(self.data):---> 12 raise StopIteration 13 else: 14 data = self.data[self.ind]
從next函數中只能向前取資料,一次取一個可以看出來,不過不能重複取資料,那這個可不可以解決呢。
我們知道iterator只能迭代一次,但是iterable對象則沒有這個限制,因此我們可以把iterator從資料中分離出來,分別定義一個iterable與iterator如下:
class Data(object): # 只是iterable:可迭代對象而不iterator:迭代器 def __init__(self, *args): self.data = list(args) def __iter__(self): # 並沒有返回自身 return DataIterator(self)class DataIterator(object): # iterator: 迭代器 def __init__(self, data): self.data = data.data self.ind = 0 def __iter__(self): return self def next(self): if self.ind == len(self.data): raise StopIteration else: data = self.data[self.ind] self.ind += 1 return dataif __name__ == '__main__': d = Data(1, 2, 3) for x in d: print x, for x in d: print x,1,2,31,2,3
Python支援在容器上迭代,通過兩個方法實現,允許使用者自訂,序列總是支援迭代方法.
迭代器的功能可以使用列表代替,但如果有很多值,列表就會佔用太多的記憶體,而如果有可以一個接一個地計算值的函數,那麼就可以在使用時採用計算一個值時擷取一個值,佔用更少記憶體。 迭代器對於變數,有兩個方法iter(),next()
當遍曆一個迭代器的時候,它會修改內部狀態,導致你只能向前擷取下一個元素,不能通過迭代器訪問後面一個元素;也就是說當你通過迭代器訪問了一個元素以後,在當前迴圈中不能後退繼續訪問該元素了,除非你重新生產迭代器對象進行遍曆。
#迭代器對於變數list1=[1,2,3,4,5]it1=iter(list1)print(next(it1))print(next(it1))print('=============')for x in it1: print(x, end='\n')print('=============')#可以使用next()函數list2=list('abcdefg')it2=iter(list2)while True: try: print(next(it2)) except StopIteration: sys.exit()
輸出結果如下:
12=============345=============abcdefg
對於要返回迭代器的類,要實現iterator.iter(),iterator.next()
迭代器對象被要求支援下面的兩個方法,合起來形成迭代協議。
iterator._iter_()
返回迭代器對象自身。為了允許容器和迭代器被用於for和in語句中,必須實現該方法。
iterator._next_()
返回容器的下一個條目。如果沒有更多的條目,拋出StopIteration異常
另外需要注意的是在迭代器中next方法是return下一個元素的值,不像下面介紹的產生器yield一個元素
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 selffibs=Fibs()for f in fibs: if f > 20: break
產生器(generator)
任何使用yield的函數都稱之為產生器.
首先需要明確的就是產生器也是iterator迭代器,因為它遵循了迭代器協議.產生器函數跟普通函數只有一點不一樣,就是把 return 換成yield,其中yield是一個文法糖,內部實現了迭代器協議,同時保持狀態可以掛起。
(如果換成return 函數就返回了)
另外一種說法:產生器就是一個返回迭代器的函數,與普通函數的區別是產生器包含yield語句,更簡單點理解產生器就是一個迭代器。
使用yield,可以讓函數產生一個序列,該函數返回的物件類型是”generator”,通過該對象連續調用next()方法返回序列值。
產生器函數只有在調用_next()_方法的時候才開始執行函數裡面的語句.
在調用count函數時:c=count(7),並不會列印”counting”只有等到調用c.next()時才真正執行裡面的語句。每次調用next()方法時,count函數會運行到語句yield n處為止,next()的傳回值就是產生值n,此處為了查看傳回值列印了出來,可以去掉,再次調用next()方法時,函數繼續執行yield之後的語句(熟悉Java的朋友肯定知道Thread.yield()方法,作用是暫停當前線程的運行,讓其他線程執行),如:
yield作用就是返回一個產生器,它會儲存當前函數狀態,記錄下一次函數被調用next的時候運行狀態
a = (i for i in range(4))print(a)for i in a: print(i)for i in a: print('==') print(i)只輸出0,1,2,3
def count(n): print("cunting") while n > 0: print('before yield') yield n # 產生值:n print(n) n -= 1 print('after yield')c=count(7)c.__next__()#before yieldc.__next__()# 7# after yield# before yieldc.__next__()# 6# after yield# before yield
如果一直調用next方法,當執行到沒有可迭代的值後,程式就會報錯:
Traceback (most recent call last): File "", line 1, in StopIteration
所以一般不會手動的調用next方法,而使用for迴圈
for i in count(5): print (i)
def fibonacci(n): # 產生器函數 - 斐波那契 a, b, counter = 0, 1, 0 while True: if counter > n: return yield a a, b = b, a + b counter += 1# f 是一個迭代器,由產生器返回產生f = fibonacci(10)while True: try: print(next(f), end=" ") except StopIteration: sys.exit()
def fibonacci(): a=b=1 yield a while True: a,b = b,a+b yield b# f=fibonacci()# print(type(fibonacci()))# f.__next__()# f.__next__()for num in fibonacci(): if num > 100: break print (num) time.sleep(1)
可以調試看輸出,就明白了。
另外還有一種定義產生器的方法:產生器運算式”()”
my_generator = (x for x in range(3))print(type(my_generator))for i in my_generator: print(i)