Python筆記003-生成器和生成器表達式
以下是我學習《流暢的Python》后的個人筆記,現在拿出來和大家共享,希望能幫到各位Python學習者。
首次發表于: 微信公眾號:科技老丁哥,ID: TechDing,敬請關注。
本篇主要知識點:
-
生成器使用yield做關鍵字,一次只返回一個值給調用者,然后暫停執行,其作用是:節省內存空間。
-
生成器可以用next()函數,也可以用for迭代的方式獲取元素值,中間還可以用close()來隨時終止生成器。
-
生成器表達式可以認為是一種特殊的生成器,其代碼更簡潔,更容易理解,且和別的函數結合會更加靈活。
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
)
)
#
# 獲取生成器中的元素值
for
value
in
squares
(
5
)
:
# 行為類似于迭代器,循環獲取元素值
print
(
'value: '
,
value
)
生成器并不像一般的函數,它返回一個值后,生成器函數會自動掛起,等到下一次調用時(使用其內部成員方法
__next__
來實現),再返回到這個函數中繼續執行。
所以要想獲取生成器的元素值,需要通過成員方法next()來進行,比如:
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
)
# 生成器表達式:
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,歡迎下載
參考資料:
- 《流暢的Python》,Luciano Ramalho (作者) 安道 , 吳珂 (譯者)。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
