迭代
可使用hasattr函數來檢測一個對象是不是可迭代的,即檢查對象中是否有__iter__方法。python
>>> hasattr(list, '__iter__') # 列表可迭代 True >>> hasattr(int, '__iter__') # 整型不可迭代 False
__iter__是一個特殊方法,它是迭代規則的基礎,有了它,就說明對象是可迭代的。
跟迭代有關的一個內建函數iter(),它返回一個迭代器對象。函數
>>> lst = list('python') # lst是可迭代的
>>> hasattr(lst, '__iter__') True >>> hasattr(lst, '__next__') # lst中沒有__next__方法,不是迭代器對象
False >>> iter_lst = iter(lst) # 由lst產生一個迭代器對象iter_lst,等同於iter_lst = lst.__iter__()
>>> hasattr(iter_lst, '__iter__') True >>> hasattr(iter_lst, '__next__') # iter_lst中有__next__屬性,是迭代器對象
True
可迭代的對象中有__iter__方法,但不必定是迭代器對象;迭代器對象必定是可迭代的,它同時擁有__iter__方法和__next__方法。
迭代器對象要遵循迭代協議,即實現了__next__方法。咱們能夠直接在類中定義__next__方法以實現迭代器協議,也能夠經過執行對象名.__iter__()的方式實現迭代器,還能夠經過執行python的內建函數iter(),即iter(對象名)的方式實現迭代器對象。spa
迭代器
迭代器能夠用來幫助咱們記錄每次迭代訪問到的位置,當咱們對迭代器使用next()函數的時候,迭代器會返回它所記錄位置的下一個位置的數據。實際上,在使用next()函數的時候,調用的就是迭代器對象的__next__方法(Python3中是對象的__next__方法,Python2中是對象的next()方法)。
因此,咱們要想構造一個迭代器,就要實現它的_next_方法。但這還不夠,python要求迭代器自己也是可迭代的,因此咱們還要爲迭代器實現__iter__方法,而__iter__方法要返回一個迭代器,迭代器自身正是一個迭代器,因此迭代器的__iter__方法返回自身self便可。
編寫一個迭代器對象。code
>>> class MyRange: ... def __init__(self, n): ... self.i = 1 ... self.n = n ... def __iter__(self): # 實現__iter__方法 ... return self ... def __next__(self): # 實現__next__方法 ... if self.i <= self.n: ... i = self.i ... self.i += 1 ... return i ... else: ... raise StopIteration # 條件結束,必須返回StopIteration異常 ... >>> x = MyRange(7) >>> print([i for i in x]) [1, 2, 3, 4, 5, 6, 7] >>> print(hasattr(x, '__iter__')) # 自定義的迭代器對象中有__iter__方法和__next__方法 True >>> print(hasattr(x, '__next__')) True
以上代碼是仿寫相似range()的類,但它跟range()又有所不一樣,它是一個迭代器對象,有如下特色:
__iter__()方法是類中的核心,它返回了迭代器自己。一個實現了__iter__方法的對象,就意味着它是可迭代的。
實現了__next__()方法,使得這個對象是迭代器對象。可迭代的對象不必定是迭代器對象,如列表、元組等;迭代器對象必定可迭代。
再實現一個斐波那契數列的迭代器對象。利用迭代器對象實現斐波那契數列的優勢是節省內存,迭代器對象的斐波那契數列不會將數列的全部值都存放在內存中,只會一個一個地取出,並且取出了後一個,前一個值不會保存在內存值。對象
>>> class Fibs: ... def __init__(self, max_num): ... self.max_num = max_num ... self.a = 0 ... self.b = 1 ... def __iter__(self): ... return self ... def __next__(self): ... fib = self.a ... if fib > self.max_num: ... raise StopIteration ... self.a, self.b = self.b, self.a + self.b ... return fib ... >>> fibs = Fibs(7) >>> print(list(fibs)) [0, 1, 1, 2, 3, 5]
結合上面的斐波那契數列的迭代器對象,對迭代器作歸納:
1.在python中,迭代器是遵循迭代協議的對象。
2.可使用iter()函數從任何序列獲得迭代器(如list、tuple、dict等)。
3.本身編寫迭代器對象,即編寫類,其中實現__iter__()和__next__()方法。當沒有元素時,引起StopIteration異常。
4.若是有不少值,列表會佔用太多的內存,而迭代器則佔用更少內存,由於__next__機制,執行一次給一個值,內存當中只會保存當前__next__給定的值,前面的值不會被保存,因此節省內存空間。
5.迭代器從第一個元素開始訪問,直到全部元素被訪問完結束,只能往前,不能後退。且迭代器中的元素是一次性的,只能被取出一次。blog
>>> lst = [x for x in range(7)] # 列表解析式的結果依然是列表對象 >>> lst [0, 1, 2, 3, 4, 5, 6] >>> lst # 能夠無數次被調用 [0, 1, 2, 3, 4, 5, 6] >>> tu = (x for x in range(9)) # 元組解析式是一個迭代器對象 >>> tu <generator object <genexpr> at 0x10218a1b0> >>> list(tu) # 迭代器對象中的內容只能被調用一次,是一次性的 [0, 1, 2, 3, 4, 5, 6, 7, 8] >>> list(tu) # 迭代器對象只能向前,不能後退 [] >>> tu2 = (x for x in range(3)) >>> tu2.__next__() # 迭代器對象就是經過調用它的__next__方法來取值 0 >>> tu2.__next__() 1 >>> tu2.__next__() 2 >>> tu2.__next__() # 沒有元素則拋出StopIteration異常 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> tu3 = (x for x in range(3)) >>> next(tu3) # 經過內置函數來取出迭代器中的元素 0 >>> next(tu3) # next(tu3)就至關於執行tu3.__next__() 1 >>> next(tu3) # 內置函數next()就是在執行迭代器中的__next__方法 2 >>> next(tu3) # 沒有元素則拋出StopIteration異常 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> next(tu3, ‘aa’) # next()函數可提供一個默認值,沒有元素時輸出默認值 aa
for循環和迭代器:
對一個可迭代對象執行for循環遍歷,首先for循環會對對象調用iter()函數,iter()函數會返回一個定義了__next__方法的迭代器對象,再經過對對象調用next()函數(next()函數會執行對象內部的__next__方法)來逐個訪問對象的內容。next()也是python內置函數。在沒有後續元素時,next()會拋出一個StopIteration異常,通知for語句循環結束。內存
>>> l = list('python') # 可迭代的列表對象,對象中有__iter__方法 >>> for i in l: # 執行for循環,會對對象l調用iter()函數,返回一個迭代器對象 ... print(i, end=' ') # 而後再對迭代器對象調用next()函數取值 ... p y t h o n