Python中的閉包是什么芯杀?

目錄

  • 作用域(scope)
  • 閉包的定義
  • 閉包的作用
  • 總結(jié)

2018.4.15
更新了對于函數(shù)作用域的理解內(nèi)容

1.作用域

作用域限定了一個變量在程序中的有效范圍肤舞,對于一個函數(shù),其內(nèi)部定義的變量的作用域就是這個函數(shù)體內(nèi)部项炼,一旦函數(shù)結(jié)束被調(diào)用担平,此變量將被析構(gòu)。簡言之锭部,函數(shù)中定義的變量稱作局部變量暂论,它只能在函數(shù)內(nèi)部被引用。

下面的例子說明了局部變量不能在函數(shù)外訪問:

In [9]: def f1():
   ...:     a = 1
   ...:

In [10]: print(a)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-10-bca0e2660b9f> in <module>()
----> 1 print(a)

NameError: name 'a' is not defined

而在所有函數(shù)之外定義的變量可以被函數(shù)訪問:

In [12]: b = 1

In [13]: def f2():
    ...:     print(b)
    ...:

In [14]: f2()
1

另外拌禾,由于Python支持函數(shù)的嵌套取胎,按照函數(shù)的作用域原則,內(nèi)函數(shù)可以訪問外函數(shù)定義的變量:

In [37]: def f1():
    ...:     x = 1
    ...:     def f2():
    ...:         print(x)
    ...:     f2()
    ...:

In [38]: f1()
1

2.閉包的定義

我們將上一個代碼稍作修改:

In [50]: def f1():
    ...:     x = 1
    ...:     def f2():
    ...:         print(x)
    ...:     return f2
    ...:

In [51]: m = f1()

In [52]: n = f1()

In [53]: m == n
Out[53]: False
    
In [54]: m()
Out [54]: 1
  • 從上面的例子可以看出湃窍,f1每次返回的函數(shù)都不同闻蛀。
  • 還有一個隱含特性,在In [54]: x()這條代碼中您市,盡管隨著函數(shù)f1()被調(diào)用觉痛,參數(shù)x的作用域已經(jīng)結(jié)束,然而函數(shù)m仍然可以正常的引用參數(shù)x茵休,這表明了內(nèi)嵌函數(shù)可以引用外部函數(shù)的參數(shù)薪棒,并且當(dāng)這些參數(shù)的作用域隨著外部函數(shù)的調(diào)用結(jié)束后仍然能被內(nèi)嵌函數(shù)引用。

通過上述的例子榕莺,我們再引入Wiki百科的對于閉包的定義:

在計算機科學(xué)中俐芯,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures)帽撑,是引用了自由變量的函數(shù)泼各。這個被引用的自由變量將和這個函數(shù)一同存在鞍时,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外亏拉。另一種說法認為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。

如果要在內(nèi)嵌函數(shù)中修改引用的外部變量逆巍,需要在內(nèi)嵌函數(shù)中對要修改的變量使用nonlocal關(guān)鍵字進行聲明:

In [58]: def creatCounter():    # 實現(xiàn)一個加法器 每次調(diào)用內(nèi)嵌函數(shù)就加一
    ...:     i = 0
    ...:     def count():
    ...:         nonlocal i     # nonlocal關(guān)鍵字標(biāo)志著內(nèi)嵌函數(shù)將修改外部變量 如果沒有這行代碼將會出錯
    ...:         i += 1
    ...:         return i
    ...:     return count
    ...:

In [59]: f = creatCounter()

In [60]: f()
Out[60]: 1

In [61]: f()
Out[61]: 2

In [62]: f()
Out[62]: 3

3.閉包的作用

閉包避免使用了全局變量及塘,閉包允許將某些數(shù)據(jù)和函數(shù)關(guān)聯(lián)起來,這一點很像類锐极。在面向?qū)ο筮^程中结缚,我們將定義了一些屬性艰匙,并將它們與一些方法關(guān)聯(lián)起來。如果要定義只用一個方法的類饮亏,可以采用閉包的方法實現(xiàn)。另外脓豪,閉包在裝飾器中也很重要。

判斷一個函數(shù)是不是閉包,可以查看它的closure屬性净薛,如果是閉包,查看該屬性將會返回一個cell對象組成的元組蒲拉,分別對每個cell對象查看其cell_contents屬性肃拜,返回的內(nèi)容就是閉包引用的自由變量的值。下面通過一個例子展示:

In [41]: def add(x,y):
    ...:     def f(z):
    ...:         return x+y+z
    ...:     return f
    ...:

In [42]: d = add(5,5)

In [43]: d(9)
Out[43]: 19

In [44]: d(1)
Out[44]: 11

In [45]: d.__closure__      # 閉包的__closure__屬性
Out[45]:
(<cell at 0x000001F9A295CCD8: int object at 0x000000006F666140>,
 <cell at 0x000001F9A295C9A8: int object at 0x000000006F666140>)

In [46]: for i in d.__closure__:
    ...:     print(i.cell_contents)   # 查看每個cell對象的內(nèi)容 —— cell_contents屬性
    ...:
5
5
  • cell_contents解釋了局部變量在脫離函數(shù)后仍然可以在函數(shù)之外被訪問雌团,因為變量被存儲在cell_contents中了燃领。

4.總結(jié)

  • 在python的作用域規(guī)則里面,創(chuàng)建變量一定會在當(dāng)前作用域里創(chuàng)建一個變量锦援,但是訪問或者修改變量時會先在當(dāng)前作用域查找變量猛蔽,沒有找到匹配變量的話會依次向上在閉合的作用域里面進行查找。
  • 在內(nèi)嵌函數(shù)中使用nonlocal關(guān)鍵字對外部變量進行聲明灵寺,可以允許內(nèi)嵌函數(shù)修改外部變量枢舶。
  • 閉包是引用了自由變量的函數(shù)。
  • 閉包在運行時可以有多個實例替久,不同的引用環(huán)境和相同的函數(shù)組合可以產(chǎn)生不同的實例凉泄。
  • 閉包中包含了內(nèi)部函數(shù)的代碼,以及所需外部函數(shù)中的變量的引用蚯根。其中所引用的變量稱作上值(upvalue)后众。
  • 判斷一個函數(shù)是不是閉包,可以查看它是否存在closure屬性
  • closure屬性是一個包含若干個cell對象的元組颅拦,每個cell對象的cell_content屬性分別代表一個自由變量的值蒂誉。

結(jié)語:這篇文章更多的只是作為學(xué)習(xí)筆記,其中參考了網(wǎng)上一些前輩的文章或是其他資料距帅,所以有些可能會大量引用右锨,如有冒犯,請私信我碌秸,還望諒解绍移。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市讥电,隨后出現(xiàn)的幾起案子蹂窖,更是在濱河造成了極大的恐慌,老刑警劉巖恩敌,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞬测,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機月趟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門灯蝴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人孝宗,你說我怎么就攤上這事绽乔。” “怎么了碳褒?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵折砸,是天一觀的道長。 經(jīng)常有香客問我沙峻,道長睦授,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任摔寨,我火速辦了婚禮去枷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘是复。我一直安慰自己删顶,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布淑廊。 她就那樣靜靜地躺著逗余,像睡著了一般。 火紅的嫁衣襯著肌膚如雪季惩。 梳的紋絲不亂的頭發(fā)上录粱,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音画拾,去河邊找鬼啥繁。 笑死,一個胖子當(dāng)著我的面吹牛青抛,可吹牛的內(nèi)容都是我干的旗闽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蜜另,長吁一口氣:“原來是場噩夢啊……” “哼适室!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蚕钦,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤亭病,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嘶居,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年邮屁,在試婚紗的時候發(fā)現(xiàn)自己被綠了整袁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡佑吝,死狀恐怖坐昙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芋忿,我是刑警寧澤炸客,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站戈钢,受9級特大地震影響痹仙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜殉了,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一开仰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧薪铜,春花似錦众弓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蜒滩,卻和暖如春傻粘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帮掉。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工弦悉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蟆炊。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓稽莉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親涩搓。 傳聞我的和親對象是個殘疾皇子污秆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 〇、前言 本文共108張圖昧甘,流量黨請慎重良拼! 歷時1個半月,我把自己學(xué)習(xí)Python基礎(chǔ)知識的框架詳細梳理了一遍充边。 ...
    Raxxie閱讀 18,952評論 17 410
  • 閉包是函數(shù)式編程的重要語法結(jié)構(gòu)庸推,Python是以函數(shù)對象為基礎(chǔ)的常侦,為閉包這一語法結(jié)構(gòu)提供支持。在學(xué)習(xí)的起初贬媒,我也不...
    狗子渣渣閱讀 347評論 2 4
  • 每天我都會路過那座山 有時是迷霧彌漫的清晨 山在陽光下半隱著 有時是日光消散的夜晚 山在黑暗中靜默著 秋天到了 山...
    楊無悔閱讀 428評論 0 0
  • 圖片發(fā)自簡書App 一 汝凌有一頭到腰的長發(fā)聋亡,水嫩的皮膚好似能捏出水來。她高高的個子际乘,纖細白白的長腿坡倔,走在大街上都...
    南東尼閱讀 1,346評論 10 23
  • 有誰不曾為那暗戀而痛苦养葵?我們總以為那份癡情很重征堪,很重,是世上最重的重量港柜。有一天请契,暮然回首,我們才發(fā)現(xiàn)夏醉,它一直都是很...
    有夢心飛揚閱讀 739評論 0 3