1. 程式人生 > >Python迭代器Iterator和生成器generator

Python迭代器Iterator和生成器generator

容器(container)

容器是一種把多個元素組織在一起的資料結構,容器中的元素可以逐個地迭代獲取,可以用innot in關鍵字判斷元素是否包含在容器中。通常這類資料結構把所有的元素儲存在記憶體中(也有一些特例,並不是所有的元素都放在記憶體,比如迭代器和生成器物件)

可迭代物件(iterable)

很多容器都是可迭代物件,此外還有更多的物件同樣也是可迭代物件,比如處於開啟狀態的files,sockets等等。但凡是可以返回一個迭代器的物件都可稱之為可迭代物件,例如:

>>> x = [1, 2, 3]
>>> y = iter(x)
>>> z = iter(x)
>>> next(y)
1
>>> next(y)
2
>>> next(z)
1
>>> type(x)
<class 'list'>
>>> type(y)
<class 'list_iterator'>

這裡x是一個可迭代物件,可迭代物件和容器一樣是一種通俗的叫法,並不是指某種具體的資料型別,list是可迭代物件,dict是可迭代物件,set也是可迭代物件。yz是兩個獨立的迭代器,迭代器內部持有一個狀態,該狀態用於記錄當前迭代所在的位置,以方便下次迭代的時候獲取正確的元素。迭代器有一種具體的迭代器型別,比如list_iteratorset_iterator。可迭代物件實現了__iter__方法,該方法返回一個迭代器物件。

迭代器(iterator)

什麼是迭代器

迭代器是訪問可迭代物件的工具
迭代器是指用iter(obj) 函式返回的物件(例項)
迭代器可以用next(it) 函式獲取可迭代物件的資料

任何實現了__iter____next__()(python2中實現next())方法的物件都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一個值

迭代器函式iter()和next()

iter(iterable)  :  從可迭代物件中返回一個迭代器,iterable 必須是能提供一個迭代器的物件
next(iterator) :  從迭代器iterator中獲取下一個記錄,如果無法獲取下一條記錄,則觸發StopIteration異常

說明:
    迭代器只能向前取值,不會後退
    用iter函式可以返回一個可迭代物件的迭代器
作用:


    迭代器物件能用next函式獲取下一個元素.

為了更直觀地感受迭代器內部的執行過程,我們自定義一個迭代器,以斐波那契數列為例: 

class Fib:
    def __init__(self):
        self.prev = 0
        self.curr = 1
 
    def __iter__(self):
        return self
 
    def __next__(self):
        value = self.curr
        self.curr += self.prev
        self.prev = value
        return value
 
>>> f = Fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Fib既是一個可迭代物件(因為它實現了__iter__方法),又是一個迭代器(因為實現了__next__方法)。例項變數prevcurr使用者維護迭代器內部的狀態。每次呼叫next()方法的時候做兩件事:

  1. 為下一次呼叫next()方法修改狀態
  2. 為當前這次呼叫生成返回結果

生成器(generator)

生成器其實是一種特殊的迭代器,不過這種迭代器更加優雅。它不需要再像上面的類一樣寫__iter__()__next__()方法了,只需要一個yiled關鍵字。 生成器一定是迭代器(反之不成立),因此任何生成器也是以一種懶載入的模式生成值

用生成器來實現斐波那契數列的例子如下:

def fib():
    prev, curr = 0, 1
    while True:
        yield curr
        prev, curr = curr, curr + prev
 
>>> f = fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

fib就是一個普通的python函式,它特殊的地方在於函式體中沒有return關鍵字,函式的返回值是一個生成器物件當執行f=fib()返回的是一個生成器物件,此時函式體中的程式碼並不會執行,只有顯示或隱示地呼叫next的時候才會真正執行裡面的程式碼。

生成器在Python中是一個非常強大的程式設計結構,可以用更少地中間變數寫流式程式碼,此外,相比其它容器物件它更能節省記憶體和CPU,當然它可以用更少的程式碼來實現相似的功能

什麼是生成器:

生成器是能夠動態提供資料的物件,生成器物件也是可迭代物件(例項)

生成器有兩種:
  1. 生成器函式
  2. 生成器表示式

生成器函式的定義

含有yield語句的函式是生成器函式,此函式被呼叫將返回一個生成器物件
 yield 翻譯為(產生或生成)

yield 語句

語法:
    yield 表示式
說明:
    yield 用於 def 函式中,目的是將此函式作用生成器函式使用
    yield 用來生成資料,供迭代器的next(it) 函式使用

生成器函式說明:

生成器函式的呼叫將返回一個生成器物件,生成器物件是一個可迭代物件
 在生成器函式呼叫return 會觸發一個StopIteration異常

因此在程式碼中類似如下程式碼可以重構優化 :

def something():
    result = []
    for ... in ...:
        result.append(x)
    return result

可以優化為:

def iter_something():
    for ... in ...:
        yield x

下面方法較上面方法更節省CPU和記憶體

 生成器表示式:

語法:
    (表示式 for 變數 in 可迭代物件 [if 真值表達式 ])
 說明:
    if 子句可以省略
 作用:
    用推導式的形式建立一個新的生成器

 迭代工具函式

迭代工具函式的作用是生成一個個性化的可迭代物件 

函式:

zip(iter1[, iter2[, ...]])                  返回一個zip物件,此物件用於生成元組,此元組的個數由最小的可迭代物件決定

enumerate(iterable[, start])       生成帶索引的列舉物件,返回迭代型別為索引-值對(index-value對),預設索引從零開始,也可以用start指定