目錄
- 作用域(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)上一些前輩的文章或是其他資料距帅,所以有些可能會大量引用右锨,如有冒犯,請私信我碌秸,還望諒解绍移。