迭代器

迭代
  可使用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
相關文章
相關標籤/搜索