1. 程式人生 > >《Python基礎教程》筆記---生成器

《Python基礎教程》筆記---生成器

  •  生成器

生成器可以說是一個相對較新的python概念,python直譯器會將帶有yield的函式視為生成器處理。雖然說生成器可以幫你編寫出比較優雅的程式碼,但一般編寫的程式,都可以不使用生成器(因為基本可以用其他程式碼替代)

不過,生成器是記憶體友好型的,在處理大資料,佔用大記憶體的情況,還是有很大的用處。

 生成器由兩部分組成:生成器的函式和生成器的迭代器

  • 生成器的函式:由def語句定義其中包括yield
  • 生成器的迭代器:這個函式返回的結果

生成器通過yield生成一個值,每次一個。yield返回一箇中間值給呼叫者後就會暫停執行下面的程式碼,等待下次被重新喚醒,喚醒後便從停止的位置開始繼續執行。與普通函式的差別在於:普通函式使用return返回一個值,而yield可以生成多個值,每次一個。

接下來,用例子來介紹下生成器的具體作用

def pop(conut):
    a,b=0,1
    g=True
    while g:
        yield a+b #返回值a+b,並暫停執行下面程式碼
        a,b=a+b,b*2
        conut=conut-1
        if conut<0:
            g=False

p=pop(4)
print(p.__next__()) # 輸出1
print(p.__next__()) # 輸出3
print(p.__next__()) # 輸出7
print(p.__next__()) # 輸出15
print(p.__next__()) # 輸出31
print(p.__next__()) # StopIteration錯誤,停止迭代

注意點:是python3.6版本中。生成器的next()方法已經無法使用,相應的替代為了__next__(self);   同時,p=pop(4)返回的是一個generator物件,而p=pop返回的是一個function物件

  • 由上述程式碼可知,通過p=pop(4)---->p.__next__(),逐個獲取迭代的值,每次一個。在處理一個巨大的資料集時,相對於使用普通函式一次性獲取所有值的方式而言,採用生成器將有效的降低記憶體佔用的問題。

生成器特性 

  • __next__(),逐個獲取yield彈出的值
  • send(),傳遞引數,實現與生成器的互動
  • throw(),用於在生成器中引發異常
  • close(),用於關閉生成器

生成器表示式(列表推導的擴充套件)

  • 生成器表示式:(表示式 for filename in file)
  • 列表推導:[表示式 for filename in file]

生成器表示式返回一個生成器物件,列表推導返回一個列表型別物件,語法上的差別在於,一個外部使用的是圓括號,一個是中括號,列表推導的不足在於:它必須一次性生成所有資料,生成一個列表物件,不適合迭代巨大量的資料集

例子:

#列表推導
f=[i*2 for i in rangr(a,b)]
sum(f)

#生成器表示式
f=(i*2 for i in rangr(a,b))
sum(f)

#生成器表示式簡化,直接在一對既有的圓括號內使用生成器表示式時,可棄掉原有的圓括號
sum(i*2 for i in rangr(a,b))

處理巢狀列表

  • 遞迴生成器

處理任意層巢狀的列表(eg:[[[1],2],3,4,[5,[6,7]],8]),可以採用遞迴方式處理。

例子:

def f(nes):
    try:
        for sub in nes:
            for elem in f(sub):
                yield elem
    except TypeError:
        yield nes
  • 呼叫函式f時,有兩種可能性:
  1. 當引數是一個數,而不是一個列表時,try內會引發TypeError異常,直接跳出執行except,生成一個數
  2. 當引數是一個巢狀列表時,呼叫try內的遞迴函式,遍歷所有子列表並對它們呼叫f函式,隨後展開子列表的所有元素 
  • 函式f無法對字串物件進行迭代,因為字串物件是可以展開的,同時,字串的第一個元素是一個長度為1的字串,而長度為1的字串的第一元素是字串本身,因此這樣將會導致無窮的遞迴。

改進:

def f(nes):
    try:
        #nes不是字串物件時,跳過此段程式碼,執行下一段;
        #檢測到是一個字串物件時,執行此程式碼,引發TypeError異常,跳出try,直接用外部的except的yield生成這個字串物件
        try:nes+''
        except TypeError:pass
        else:raise TypeError


        for sub in nes:
            for elem in f(sub):
                yield elem
    except TypeError:
        yield nes
  •  普通函式模擬生成器

def f(nes):
    result=[]
    try:
        #nes不是字串物件時,跳過此段程式碼,執行下一段;
        #檢測到是一個字串物件時,執行此程式碼,引發TypeError異常,跳出try,直接用外部的except的append()方法將得到的值新增進result列表
        try:nes+''
        except TypeError:pass
        else:raise TypeError


        for sub in nes:
            for elem in f(sub):
                result.append(elem)
    except TypeError:
        result.append(nes)
    return result