1. 程式人生 > >python引入的新概念—生成器

python引入的新概念—生成器

最近在開發一個數據轉換工具,資料吞吐量有100w,在對資料讀取處理的時候儲存到列表裡,導致記憶體溢位,最終發現了生成器這個好東西,完美解決大資料處理,記憶體消耗問題

一、什麼是生成器?(generator)

呼叫函式過程中:如果在程式中,遇到函式異常,結束,return關鍵字等,函式就會結束,一旦重新呼叫該函式,一切重新開始;而最簡單的生成器就是協程,生成器是個特殊的東西,之所以說它特殊,是因為它可以控制迴圈的迭代行為,每當函式執行到生成器,生成器就會掛起,返回給呼叫程式一個值,同時保留了函式的狀態,等待下一次呼叫!

這種保留函式狀態的能力讓生成器生成值的序列變得非常簡單

二、生成器函式和普通函式的區別

如下是求給定列表的偶數集合,只調用了一次函式

def getEven(list):
    L = []
    for element in list:
        if element%2==0:
            L.append(element)
    return L
print getEven([1,2,3,4,5,6,67])
[2, 4, 6]

如果使用生成器,迴圈呼叫生成器函式,每一次生成一個值,保留函式狀態(迴圈的elelment值),等待下一次呼叫,如圖所示函式一共被呼叫了三次

number = 0
def getEven(list):
    L = []
    for element in list:
        if element%2==0:
            global number
            number+=1
            yield element
for element in getEven([1,2,3,4,5,6,67]):
    print element,number
2 1
4 2
6 3

三、為什麼使用生成器?

我在開發python資料轉換小工具時發現,在做處理大資料吞吐時,如果需要臨時儲存資料到列表或者其它什麼,極其消耗記憶體,很可能會造成記憶體不夠導致程式癱瘓,工具無法處理大資料!

例如上面舉的例子,如果我要對每一個偶數進行+1處理

普通函式:需要暫時儲存整個列表,在對整個列表進行處理(可能有同學會說可以在得到資料的函式裡直接對每一個得到的資料進行處理,那如果資料是前後依賴的呢?類似於斐波那契數列呢?),這樣列表就會佔用很大記憶體

def getEven(list):
    L = []
    for element in list:
        if element%2==0:
            L.append(element)
    return L
a = getEven([1,2,3,4,5,6,67])
print a
for i in range(len(a)):
    a[i]+=1
print a
[2, 4, 6]
[3, 5, 7]

生成器函式:每得到一個數據,就拿出來進行+1處理,然後呼叫函式得到下一個資料,釋放上一個資料佔用的記憶體(類似的,從檔案衝讀取大資料,一部分一部分的處理的會大大節省記憶體)

number = 0
def getEven(list):
    L = []
    for element in list:
        if element%2==0:
            global number
            number+=1
            yield element
for element in getEven([1,2,3,4,5,6,67]):
    element+=1
    print element
    
3
5
7

結語:

  • 生成器(generator)是用來產生值的序列的
  • generator就是一個特殊的迭代器
  • generator類似於return,執行到這就返回,但是功能完全不同
  • generator可以保留函式當前狀態,並返回一個值
  • ......