先來(lái)做兩個(gè)實(shí)驗(yàn),加深一下對(duì)外層變量和內(nèi)層變量的區(qū)別和關(guān)聯(lián):
>>> def funa():
x = 88
def funb():
x = 99
return print(x)
>>> funa()
88
----------
在這個(gè)案例中,雖然內(nèi)層函數(shù)定義了另一個(gè)變量x,但最終返回的,仍然是外層函數(shù)的變量x。因此x = 88.
>>> def funa():
x = 88
def funb():
x = 99
print(x)
return funb()
>>> funa()
99
----------
在這個(gè)案例中,print(x)是funb()的一部分,因此最后返回的是funb()中定義的x,因此是99.
上面兩個(gè)例子都有一個(gè)共同點(diǎn),就是內(nèi)層函數(shù)和外層函數(shù)都沒(méi)有設(shè)定參數(shù)。如果函數(shù)設(shè)定了參數(shù),會(huì)是怎么樣的情況呢?這個(gè)等下舉例的時(shí)候會(huì)提到。
閉包:如果一個(gè)內(nèi)層函數(shù)調(diào)用了他的外層函數(shù)的參數(shù)。那么我們稱(chēng)這個(gè)內(nèi)層函數(shù)和被他調(diào)用的參數(shù)為一個(gè)閉包。

舉個(gè)例子:
>>> def funa(x):
def funb(y):
return x * y
return funb
>>> a = funa(9)
>>> a(6)
54
從上面例子可以看到,x這個(gè)外層函數(shù)的參數(shù),在內(nèi)層函數(shù)中被調(diào)用。最終返回的是funb,而非funb(),原因是funb這個(gè)內(nèi)層函數(shù)是有參數(shù)y的,如果返回funb()就相當(dāng)于缺少了y,沒(méi)有給y這個(gè)參數(shù)賦值,最終就會(huì)導(dǎo)致報(bào)錯(cuò)。
>>> def funa(x):
def funb(y):
return x * y
return funb()
>>> a = funa(8)
Traceback (most recent call last):
File "<pyshell#17>", line 1, in <module>
a = funa(8)
File "<pyshell#16>", line 4, in funa
return funb()
TypeError: funb() missing 1 required positional argument: 'y'
一開(kāi)始我完全沒(méi)有看懂為什么需要輸入一個(gè)a = funa(8),然后在輸入一個(gè)a(9),后來(lái)明白了。輸入funa(8)相當(dāng)于調(diào)用了funa(x),并且將x賦值為8,外層函數(shù)內(nèi)部嵌套了一個(gè)內(nèi)層函數(shù)funb(y),運(yùn)行到這里的時(shí)候,就需要你給funb(y)里面的y一個(gè)賦值。那么這個(gè)賦值要如何賦呢?肯定不能直接funb(9)這樣子,因?yàn)閒unb()作為一個(gè)內(nèi)層函數(shù),是不能在全局環(huán)境中直接調(diào)用的。
而程序已經(jīng)通過(guò)設(shè)計(jì)解決了這個(gè)問(wèn)題。外層函數(shù)返回的結(jié)果本身就是funb,此時(shí)的funb就等于a(外層函數(shù)返回的結(jié)果),那么a(9)自然也就相當(dāng)于funb(9)了。
其實(shí)這么寫(xiě)可能更簡(jiǎn)單一些:
>>> def funa(x):
def funb(y):
return x * y
return funb
>>> funa(8)(9)
72
教科書(shū)上沒(méi)有這么寫(xiě),這是我通過(guò)理解之后推導(dǎo)出來(lái)的哦。我可真棒!!

正如上一節(jié)所說(shuō),python并不希望你在函數(shù)內(nèi)部修改全局變量,或者在內(nèi)層函數(shù)中修改外層函數(shù)的變量??扇绻阋灰夤滦校且膊皇遣荒芨?,只是在內(nèi)層函數(shù)中對(duì)變量賦值的時(shí)候,要做一下全局聲明,也就是要加上一個(gè)global。
在閉包中也是如此,如果你想在內(nèi)層函數(shù)中對(duì)外層函數(shù)的變量進(jìn)行修改,你需要先對(duì)變量進(jìn)行一個(gè)聲明,即在要修改的變量名前面加上nonlocal。例:
>>> def funa():
x = 5
def funb():
nonlocal x
x = x + 1
return x
return funb
>>> funa()()
6
當(dāng)然,這是python3中的用法,而在python2中可沒(méi)有這么智能。我們說(shuō)外層函數(shù)中x=5,內(nèi)層函數(shù)中,x=6,這時(shí)候內(nèi)層函數(shù)并不是改變了外層函數(shù)的變量值,而是重新生成了一個(gè)名稱(chēng)相同但id不同的變量x,并給他賦值為6。為什么要這樣操作?因?yàn)樵谶@個(gè)過(guò)程中,x這個(gè)變量是放在棧里的。因此要?jiǎng)?chuàng)造一個(gè)新的x,來(lái)避免將原來(lái)的x所覆蓋。
可我們有的時(shí)候就是要覆蓋原來(lái)的x,就是要在內(nèi)層函數(shù)中修改外層函數(shù)的變量值。那咋辦呢?可能你已經(jīng)想到主意了。我們找一個(gè)不放在棧里的東西來(lái)表示變量x不就行了嗎?
那么那種類(lèi)型的數(shù)據(jù)是不放在棧里的呢?序列!
因此,假如我們讓外層函數(shù)的x = [5],在內(nèi)層函數(shù)中,讓x[0] = x[0] + 1這就完美地逃脫了原來(lái)規(guī)則的束縛。舉例如下:
>>> def funa():
x = [5]
def funb():
x[0] = x[0] + 1
return x[0]
return funb
>>> funa()()
6
由于python3已經(jīng)幫我們解決了這個(gè)問(wèn)題了,因此這個(gè)投機(jī)取巧的辦法學(xué)習(xí)其思想,能看懂,就OK了。