Python筆記003-生成器和生成器表達式

Python筆記003-生成器和生成器表達式

如下是我學習《流暢的Python》後的我的筆記,如今拿出來和你們共享,但願能幫到各位Python學習者。git

首次發表於: 微信公衆號:科技老丁哥,ID: TechDing,敬請關注。

本篇主要知識點:

  1. 生成器使用yield作關鍵字,一次只返回一個值給調用者,而後暫停執行,其做用是:節省內存空間。github

  2. 生成器能夠用next()函數,也能夠用for迭代的方式獲取元素值,中間還能夠用close()來隨時終止生成器。數組

  3. 生成器表達式能夠認爲是一種特殊的生成器,其代碼更簡潔,更容易理解,且和別的函數結合會更加靈活。微信

1. 生成器

生成器是Python中一個特殊的程序,用於控制循環的迭代行爲。相對於通常函數用return來一次性返回全部值,生成器使用yield關鍵字,一次只返回一個值。函數

這樣的設計有很大的好處:在數據處理時,若是函數return出來的是一個很是大的數組,那麼會很是佔用內存,有時會報MemoryError的錯誤,而使用yield後一次僅僅返回一個元素值,能夠優化內存佔用的狀況。學習

從這種角度來說,生成器函數每一次調用都返回一個元素值,這種特性使得生成器長得像函數,但行爲卻像迭代器。優化

def squares(x): # 計算0-x的全部數的平方
# return [i*i for i in range(x)] # 普通寫法,一次返回一個list,包含全部元素
    for i in range(x):
        yield i*i # 生成器:一次只返回一個值
print(squares(5)) # <generator object squares at 0x00000157DBD16830>
# 獲取生成器中的元素值
for value in squares(5): # 行爲相似於迭代器,循環獲取元素值
    print('value: ',value)
複製代碼

生成器並不像通常的函數,它返回一個值後,生成器函數會自動掛起,等到下一次調用時(使用其內部成員方法__next__來實現),再返回到這個函數中繼續執行。lua

因此要想獲取生成器的元素值,須要經過成員方法next()來進行,好比:spa

square_five=squares(5)
print(next(square_five)) # 0
print(next(square_five)) # 1
print(next(square_five)) # 4
print(next(square_five)) # 9
print(next(square_five)) # 16
print(next(square_five)) # 報錯:StopIteration: 超過yield的全部元素
複製代碼

next()函數每次執行時,都會繼續執行掛起的生成器函數,直到執行完畢。設計

生成器的這種特色被稱爲"延遲計算"或"惰性求值(Lazy evaluation)",能夠有效的節省內存。惰性求值其實是體現了協同程序的思想。

雖然生成器的這種行爲相似於迭代器,但二者有較大差異,迭代器不具有這種執行-暫停-再執行-再暫停的特性,因此迭代器不具備延遲計算,沒有協同程序的思想。

使用延遲計算後,能夠極大的節省內存,好比對大文件進行讀取操做時,能夠用下列生成器方法:

## 讀取大文件的生成器方法:
def load_big_file(file_path):
    BLOCK_SIZE = 1024
    with open(file_path, 'rb') as f:
        while True:
            block = f.read(BLOCK_SIZE)
            if block:
                yield block # 一次只加載一個block到內存中,避免MemoryError
            else:
                return
複製代碼

生成器除了用next()函數來處理以外,還能夠用close()來隨時退出生成器。以下代碼:

## 使用close()能夠隨時退出生成器
square_five=squares(5)
print(next(square_five)) # 0
print(next(square_five)) # 1
print(next(square_five)) # 4
square_five.close() # 退出生成器
print(next(square_five)) # Error: StopIteration:
print(next(square_five)) # Error: StopIteration:
複製代碼

2. 生成器表達式

從形式上來看,生成器表達式和列表推導式很像,僅僅是將列表推導式中的[]替換爲(),可是二者差異挺大,生成器表達式能夠說組合了迭代功能和列表解析功能。

生成器表達式能夠認爲是一種特殊的生成器函數,相似於lambda表達式和普通函數。可是和生成器同樣,生成器表達式也是返回生成器generator對象,一次只返回一個值。

# 上面的squares函數能夠改寫爲:
# 列表推導式的寫法是:
squares_list=[i*i for i in range(5)] # 一次性返回整個list
print('列表推導式:',squares_list) # 列表推導式: [0, 1, 4, 9, 16]
# 生成器表達式:
squares2=(i*i for i in range(5)) # 生成器表達式一次返回一個值
print('生成器表達式:',squares2) # 生成器表達式: <generator object ..
print(next(squares2)) # 0
print(next(squares2)) # 1
print(next(squares2)) # 4
複製代碼

生成器表達式是一種特殊的生成器,因此它也有生成器的特性,可使用for循環來獲取元素值,for循環內部自動調用了next()函數來執行。

# generator對象能夠直接用for來獲取全部元素值
squares2=(i*i for i in range(5)) # 生成器表達式就是一個generator對象
for i in squares2:
    print('i: ',i)

# 上面能夠簡寫爲:
[print('i: ',i) for i in (i*i for i in range(5))]
複製代碼

生成器表達式若是做爲某個函數的參數,則能夠省略掉(),直接使用便可,eg:

## 若是生成器表達式整個做爲某個函數的參數,能夠省略掉()
max_value=max(i*i for i in range(5))  # 計算生成器的全部元素中的最大值
print(max_value) # 16
複製代碼

首次發表於: 微信公衆號:科技老丁哥,ID: TechDing,敬請關注。

本文全部代碼都已經上傳到個人github,歡迎下載

參考資料:

  1. 《流暢的Python》,Luciano Ramalho (做者) 安道 , 吳珂 (譯者)。
相關文章
相關標籤/搜索