Python-閉包

0. 函數(shù)相關(guān)知識

1)Python中“一切皆對象”,函數(shù)也不例外

先定義一個函數(shù):

def func():

? ? print('我是函數(shù):{}'.format(func.__name__))? # __name__屬性返回函數(shù)的名稱

打印函數(shù)名:

>>> print(func)

<function func at 0x00000209EA722E18>

返回的是函數(shù)對象func,并指出了它的內(nèi)存地址眼溶。

對函數(shù)使用type():

>>> type(func)

<class 'function'>

以上可以看出 函數(shù) func 是 function類型的一個對象,屬于可調(diào)用對象(具有__call__魔法方法)梅桩。

2)函數(shù)可以賦值給一個變量

函數(shù)作為一個對象撕阎,它可以作為其他函數(shù)的參數(shù)准浴,也可以作為函數(shù)的返回值,還能夠賦值給一個變量:

>>> f = func? ? ?

>>> f()? ? ? ? ? # 可調(diào)用對象后加()即可執(zhí)行調(diào)用

我是函數(shù):func? ? ? # func賦值給變量f斤寇,f可以作為函數(shù)被調(diào)用桶癣,但函數(shù)名仍為func

將函數(shù)賦值給變量,是將函數(shù)的引用傳遞給變量娘锁。至此牙寞,函數(shù)名和變量共同指向 function對象func。

3)函數(shù)作為函數(shù)的返回值

定義一個可變參數(shù)的求和函數(shù):

def sum(*args):

? ? result = 0

? ? for n in args:

? ? ? ? result += n

? ? return result

如果此時調(diào)用函數(shù),將會立即求和间雀。

再定義一個函數(shù)悔详,它能夠延遲求和:

def lazy_sum(*args):

? ? def sum():

? ? ? result = 0

? ? ? for n in args:

? ? ? ? ? result += n

? ? ? return result

? ? return sum? ? ? ? ? ? ? ? # 函數(shù)sum作為返回值

調(diào)用函數(shù)lazy_sum()時,返回的是求和函數(shù)sum惹挟,而不是求和結(jié)果:

>>> f = lazy_sum(1, 2, 3)

>>> f

# f = 函數(shù)lazy_sum局部空間中的sum函數(shù)

<function lazy_sum.<locals>.sum at 0x000001ECB6C19840>

當(dāng)調(diào)用函數(shù)f時茄螃,才會真正計算求和的結(jié)果:

>>> f()

6

這種嵌套定義函數(shù),具有兩個特征:

1)內(nèi)部定義的函數(shù)使用了外部函數(shù)的參數(shù)或局部變量连锯;

2)外部函數(shù)的返回值為內(nèi)部函數(shù)(注意不是返回內(nèi)部函數(shù)的調(diào)用)归苍;

最后有一點需要注意,每次調(diào)用lazy_sum()运怖,都會返回一個新的函數(shù)拼弃,即使傳入相同的參數(shù):

>>> f1 = lazy_sum(1, 2, 3)

>>> f1 = lazy_sum(1, 2, 3)

>>> f1 == f2

False? ? # f1和f2指向的是不同內(nèi)存地址的sum函數(shù)

1.閉包

1)概念:

函數(shù)嵌套定義的前提下,如果內(nèi)部函數(shù)使用了外部函數(shù)的變量驳规,并且外部函數(shù)返回值為內(nèi)部函數(shù)肴敛,此時我們就把這個內(nèi)部函數(shù)稱為“閉包”署海。

2)特征:

也即閉包的構(gòu)成條件為:

(1)函數(shù)嵌套定義為前提吗购;

(2)內(nèi)部函數(shù)使用了外部函數(shù)的變量;

(3)外部函數(shù)的返回值為內(nèi)部函數(shù)砸狞;

3)原理:

在上面的lazy_sum()函數(shù)中:

def lazy_sum(*args):

? ? def sum():

? ? ? result = 0

? ? ? for n in args:

? ? ? ? ? result += n

? ? ? return result

? ? return sum? ? ? ? ? ? ? ? # 函數(shù)sum作為返回值

lazy_sum是外部函數(shù)捻勉,sum是內(nèi)部函數(shù),此時的sum就是閉包刀森。

閉包(內(nèi)部函數(shù))sum引用了外部函數(shù)lazy_sum的變量踱启,當(dāng)lazy_sum返回sum時,其相關(guān)參數(shù)和變量就保存在了閉包sum中研底。其中的過程:定義外部函數(shù) --> 操作系統(tǒng)為外部函數(shù)分配局部空間 --> 局部空間中定義變量和內(nèi)部函數(shù) --> 為內(nèi)部函數(shù)開辟嵌套的局部空間 --> 返回內(nèi)部函數(shù)埠偿。

過程中存在嵌套的局部空間(Enclosing),對于這個嵌套的局部空間來說榜晦,外部函數(shù)所在的局部空間就相當(dāng)于其全局空間冠蒋,其中的變量也相當(dāng)于全局變量,因此內(nèi)部函數(shù)可以像調(diào)用全局變量一樣調(diào)用外部函數(shù)的變量乾胶。而也是由于這個嵌套的局部空間抖剿,使得內(nèi)部函數(shù)保存了外部函數(shù)的變量。

閉包原理復(fù)雜识窿,應(yīng)用的時候牢記三點就可以:

1)閉包(內(nèi)部函數(shù))使用了外部函數(shù)的變量斩郎,并且能夠保存外部函數(shù)的相關(guān)變量;

2)外部函數(shù)返回的是閉包函數(shù)喻频,不是一個計算結(jié)果缩宜,該函數(shù)不會立即執(zhí)行;

3)調(diào)用外部函數(shù)并賦值給變量甥温,則該變量就等于保存了外部函數(shù)相關(guān)變量的內(nèi)部函數(shù)锻煌;

4)應(yīng)用:兩個小伙伴聊天

# 定義閉包

def person(a):

? ? def inner(b):

? ? ? print('{}:{}'.format(a, b))

? ? return inner


david = person('David')? ? # 建立一個閉包對象david

jane = person('Jane')? ? ? # 建立閉包對象jane

david('天王蓋地虎')

jane('寶塔鎮(zhèn)河妖')

>>> 運行結(jié)果:

David:天王蓋地虎

Jane:寶塔鎮(zhèn)河妖

可以看到兩種現(xiàn)象:1)調(diào)用person()時膜宋,inner延遲執(zhí)行;2)inner保存了person的變量a炼幔;

5)關(guān)鍵字nonlocal

關(guān)鍵字nonlocal能夠?qū)崿F(xiàn)對閉包外部函數(shù)變量的修改操作秋茫。nonlocal --> 聲明內(nèi)部函數(shù)的變量是非本地的。類似于定義普通函數(shù)時的聲明全局變量的關(guān)鍵字global乃秀。

# 定義閉包:不使用nonlocal

def outer():

? ? a = 10

? ? def inner():

? ? ? ? a = 20

? ? ? ? result = a + 1

? ? ? ? print(result)

? ? inner()

? ? print(a)?

? ? return inner


test1 = outer()? # 調(diào)用外層函數(shù)

test1()? ? ? # 調(diào)用內(nèi)層函數(shù)

>>> 運行結(jié)果:

21

10

21? ?

#? a的值仍為10肛著,說明內(nèi)部函數(shù)沒有修改掉外部函數(shù)變量a的值

#? 此種情況下內(nèi)部函數(shù)并沒有使用外部函數(shù)的變量,而是自建了一個新的變量a

# 定義閉包:使用nonlocal

def outer():

? ? a = 10

? ? def inner():

? ? ? ? nonlocal a

? ? ? ? a = 20

? ? ? ? result = a + 1

? ? ? ? print(result)

? ? inner()

? ? print(a)

? ? return inner


test1 = outer()? # 調(diào)用外層函數(shù)

test1()? ? ? # 調(diào)用內(nèi)層函數(shù)

>>> 運行結(jié)果

21

20

21? ?

# a的值被修改為20?

2.總結(jié)

閉包是Python中函數(shù)式編程的重要語法結(jié)構(gòu)跺讯,屬于函數(shù)的高階應(yīng)用枢贿。通過它的原理也能夠?qū)崿F(xiàn)“裝飾器”的定義。

3.Enclosing 補充

當(dāng)定義閉包函數(shù)時刀脏,外部函數(shù)的局部命名空間中會存在“Enclosing作用域”局荚。閉包函數(shù)會存在于這個Enclosing作用域中,可以通過閉包函數(shù)的__closure__屬性調(diào)出愈污。Enclosing除了存放閉包函數(shù)外耀态,還會自動添加閉包函數(shù)用到的外部函數(shù)的對象,但僅限自己所用的暂雹。

上面閉包應(yīng)用的例子:

# 定義閉包

def person(a):

? ? a1 = 10

? ? def inner(b):

? ? ? ? print(inner.__closure__)? ? ? # 打印閉包函數(shù)的__closure__屬性

? ? ? ? print('{}:{}'.format(a, b))

? ? return inner

inner.__closure__ = (<cell at 0x000001DC0A6D7E88: str object at 0x000001DC0A768E30>, <cell at 0x000001DC0A6D76A8: function object at 0x000001DC0A799620>)

str object 即是inner用到的參數(shù)a首装;function? object為它自己;而變量a1由于inner不會使用杭跪,所以不在其中仙逻。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市涧尿,隨后出現(xiàn)的幾起案子系奉,更是在濱河造成了極大的恐慌,老刑警劉巖姑廉,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缺亮,死亡現(xiàn)場離奇詭異,居然都是意外死亡庄蹋,警方通過查閱死者的電腦和手機瞬内,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來限书,“玉大人虫蝶,你說我怎么就攤上這事【胛鳎” “怎么了能真?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我粉铐,道長疼约,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任蝙泼,我火速辦了婚禮程剥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘汤踏。我一直安慰自己织鲸,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布溪胶。 她就那樣靜靜地躺著搂擦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哗脖。 梳的紋絲不亂的頭發(fā)上瀑踢,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音才避,去河邊找鬼橱夭。 笑死,一個胖子當(dāng)著我的面吹牛工扎,可吹牛的內(nèi)容都是我干的徘钥。 我是一名探鬼主播衔蹲,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼肢娘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舆驶?” 一聲冷哼從身側(cè)響起橱健,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沙廉,沒想到半個月后拘荡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡撬陵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年珊皿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巨税。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡蟋定,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出草添,到底是詐尸還是另有隱情驶兜,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站抄淑,受9級特大地震影響屠凶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肆资,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一矗愧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧郑原,春花似錦贱枣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至栖秕,卻和暖如春春塌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背簇捍。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工只壳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暑塑。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓吼句,卻偏偏與公主長得像,于是被迫代替她去往敵國和親事格。 傳聞我的和親對象是個殘疾皇子惕艳,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355