Python筆記003-生成器和生成器表達(dá)式
以下是我學(xué)習(xí)《流暢的Python》后的個(gè)人筆記,現(xiàn)在拿出來(lái)和大家共享,希望能幫到各位Python學(xué)習(xí)者。
首次發(fā)表于: 微信公眾號(hào):科技老丁哥,ID: TechDing,敬請(qǐng)關(guān)注。
本篇主要知識(shí)點(diǎn):
-
生成器使用yield做關(guān)鍵字,一次只返回一個(gè)值給調(diào)用者,然后暫停執(zhí)行,其作用是:節(jié)省內(nèi)存空間。
-
生成器可以用next()函數(shù),也可以用for迭代的方式獲取元素值,中間還可以用close()來(lái)隨時(shí)終止生成器。
-
生成器表達(dá)式可以認(rèn)為是一種特殊的生成器,其代碼更簡(jiǎn)潔,更容易理解,且和別的函數(shù)結(jié)合會(huì)更加靈活。
1. 生成器
生成器是Python中一個(gè)特殊的程序,用于控制循環(huán)的迭代行為。相對(duì)于一般函數(shù)用return來(lái)一次性返回所有值,生成器使用yield關(guān)鍵字,一次只返回一個(gè)值。
這樣的設(shè)計(jì)有很大的好處:在數(shù)據(jù)處理時(shí),如果函數(shù)return出來(lái)的是一個(gè)非常大的數(shù)組,那么會(huì)非常占用內(nèi)存,有時(shí)會(huì)報(bào)MemoryError的錯(cuò)誤,而使用yield后一次僅僅返回一個(gè)元素值,可以優(yōu)化內(nèi)存占用的情況。
從這種角度來(lái)講,生成器函數(shù)每一次調(diào)用都返回一個(gè)元素值,這種特性使得生成器長(zhǎng)得像函數(shù),但行為卻像迭代器。
def
squares
(
x
)
:
# 計(jì)算0-x的所有數(shù)的平方
# return [i*i for i in range(x)] # 普通寫法,一次返回一個(gè)list,包含所有元素
for
i
in
range
(
x
)
:
yield
i
*
i
# 生成器:一次只返回一個(gè)值
print
(
squares
(
5
)
)
#
# 獲取生成器中的元素值
for
value
in
squares
(
5
)
:
# 行為類似于迭代器,循環(huán)獲取元素值
print
(
'value: '
,
value
)
生成器并不像一般的函數(shù),它返回一個(gè)值后,生成器函數(shù)會(huì)自動(dòng)掛起,等到下一次調(diào)用時(shí)(使用其內(nèi)部成員方法
__next__
來(lái)實(shí)現(xiàn)),再返回到這個(gè)函數(shù)中繼續(xù)執(zhí)行。
所以要想獲取生成器的元素值,需要通過(guò)成員方法next()來(lái)進(jìn)行,比如:
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
)
)
# 報(bào)錯(cuò):StopIteration: 超過(guò)yield的所有元素
next()函數(shù)每次執(zhí)行時(shí),都會(huì)繼續(xù)執(zhí)行掛起的生成器函數(shù),直到執(zhí)行完畢。
生成器的這種特點(diǎn)被稱為"延遲計(jì)算"或"惰性求值(Lazy evaluation)",可以有效的節(jié)省內(nèi)存。惰性求值實(shí)際上是體現(xiàn)了協(xié)同程序的思想。
雖然生成器的這種行為類似于迭代器,但兩者有較大差別,迭代器不具備這種執(zhí)行-暫停-再執(zhí)行-再暫停的特性,所以迭代器不具有延遲計(jì)算,沒有協(xié)同程序的思想。
使用延遲計(jì)算后,可以極大的節(jié)省內(nèi)存,比如對(duì)大文件進(jìn)行讀取操作時(shí),可以用下列生成器方法:
## 讀取大文件的生成器方法:
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
# 一次只加載一個(gè)block到內(nèi)存中,避免MemoryError
else
:
return
生成器除了用next()函數(shù)來(lái)處理之外,還可以用close()來(lái)隨時(shí)退出生成器。如下代碼:
## 使用close()可以隨時(shí)退出生成器
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. 生成器表達(dá)式
從形式上來(lái)看,生成器表達(dá)式和列表推導(dǎo)式很像,僅僅是將列表推導(dǎo)式中的[]替換為(),但是兩者差別挺大,生成器表達(dá)式可以說(shuō)組合了迭代功能和列表解析功能。
生成器表達(dá)式可以認(rèn)為是一種特殊的生成器函數(shù),類似于lambda表達(dá)式和普通函數(shù)。但是和生成器一樣,生成器表達(dá)式也是返回生成器generator對(duì)象,一次只返回一個(gè)值。
# 上面的squares函數(shù)可以改寫為:
# 列表推導(dǎo)式的寫法是:
squares_list
=
[
i
*
i
for
i
in
range
(
5
)
]
# 一次性返回整個(gè)list
print
(
'列表推導(dǎo)式:'
,
squares_list
)
# 列表推導(dǎo)式: [0, 1, 4, 9, 16]
# 生成器表達(dá)式:
squares2
=
(
i
*
i
for
i
in
range
(
5
)
)
# 生成器表達(dá)式一次返回一個(gè)值
print
(
'生成器表達(dá)式:'
,
squares2
)
# 生成器表達(dá)式:
print
(
next
(
squares2
)
)
# 0
print
(
next
(
squares2
)
)
# 1
print
(
next
(
squares2
)
)
# 4
生成器表達(dá)式是一種特殊的生成器,所以它也有生成器的特性,可以使用for循環(huán)來(lái)獲取元素值,for循環(huán)內(nèi)部自動(dòng)調(diào)用了next()函數(shù)來(lái)執(zhí)行。
# generator對(duì)象可以直接用for來(lái)獲取所有元素值
squares2
=
(
i
*
i
for
i
in
range
(
5
)
)
# 生成器表達(dá)式就是一個(gè)generator對(duì)象
for
i
in
squares2
:
print
(
'i: '
,
i
)
# 上面可以簡(jiǎn)寫為:
[
print
(
'i: '
,
i
)
for
i
in
(
i
*
i
for
i
in
range
(
5
)
)
]
生成器表達(dá)式如果作為某個(gè)函數(shù)的參數(shù),則可以省略掉(),直接使用即可,eg:
## 如果生成器表達(dá)式整個(gè)作為某個(gè)函數(shù)的參數(shù),可以省略掉()
max_value
=
max
(
i
*
i
for
i
in
range
(
5
)
)
# 計(jì)算生成器的所有元素中的最大值
print
(
max_value
)
# 16
首次發(fā)表于: 微信公眾號(hào):科技老丁哥,ID: TechDing,敬請(qǐng)關(guān)注。
本文所有代碼都已經(jīng)上傳到我的github,歡迎下載
參考資料:
- 《流暢的Python》,Luciano Ramalho (作者) 安道 , 吳珂 (譯者)。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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