作用域
在理解閉包前,先看一個(gè)函數(shù):
def func():
in_func = 'In Func'
print(in_func)
func()
In Func
print(in_func)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-12-bbc477aa0551> in <module>()
----> 1 print(in_func)
NameError: name 'in_func' is not defined
可以看到func()函數(shù)內(nèi)部的變量無(wú)法在函數(shù)外部使用
out_func = 'Out Func'
def func():
print(out_func)
func()
Out Func
print(out_func)
Out Func
全局變量out_func可以在函數(shù)外部和內(nèi)部使用
閉包
def closure_func():
in_func = 'In Func'
def f():
return in_func
return f
ff = closure_func() # 此時(shí)ff=f
ff() # 此時(shí)ff()=f(),函數(shù)f()訪問(wèn)了外部函數(shù)的局部變量in_func
'In Func'
此時(shí)的ff就是一個(gè)閉包,包括了f()函數(shù)和自由變量in_func
其實(shí)在ff = closure_func()
語(yǔ)句結(jié)束后函數(shù)closure_func()生命周期就結(jié)束了,但是其局部變量in_func依然存在,因?yàn)樗婚]包引用了,因此不會(huì)被回收。
維基百科對(duì)于閉包的解釋
在計(jì)算機(jī)科學(xué)中,閉包(Closure)是詞法閉包(Lexical Closure)的簡(jiǎn)稱,是引用了自由變量的函數(shù)。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。所以,有另一種說(shuō)法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。
如果在一個(gè)內(nèi)部函數(shù)里,對(duì)在外部作用域(但不在全局作用域)的變量進(jìn)行引用,那么內(nèi)部函數(shù)就被認(rèn)為是閉包(closure)。定義在外部函數(shù)內(nèi)的但由內(nèi)部函數(shù)引用或者使用的變量被稱為自由變量。
def make_power(x):
def f(y):
return y ** x
return f
power_two = make_power(2)
power_two(5)
25
power_two(3)
9
可以看到make_power()函數(shù)結(jié)束后,依然存在,被閉包引用。
所有函數(shù)都有一個(gè) __closure__
屬性,如果這個(gè)函數(shù)是一個(gè)閉包的話,那么它返回的是一個(gè)由 cell 對(duì)象 組成的元組對(duì)象。cell 對(duì)象的cell_contents 屬性就是閉包中的自由變量。
power_two.__closure__
(<cell at 0x7f9154476918: int object at 0xa68a60>,)
power_two.__closure__[0].cell_contents #自由變量存儲(chǔ)在了cell_contents中
2
創(chuàng)建一個(gè)閉包:
- 閉包函數(shù)必須有內(nèi)嵌函數(shù)
- 內(nèi)嵌函數(shù)需要引用該嵌套函數(shù)上一級(jí)namespace中的變量
- 閉包函數(shù)必須返回內(nèi)嵌函數(shù)
閉包容易出現(xiàn)的錯(cuò)誤
1.外部函數(shù)局部變量
def out_func():
out_var = 'Out Var'
def inner_func():
out_var = 'Inner Var'
return out_var
print('before: {} '.format(out_var))
inner_func()
print('after: {} '.format(out_var))
out_func()
before: Out Var
after: Out Var
閉包中函數(shù)無(wú)法修改外部函數(shù)的局部變量
要修改外部函數(shù)的局部變量,可是使用nonlocal關(guān)鍵字,修改上面的函數(shù):
def out_func():
out_var = 'Out Var'
def inner_func():
nonlocal out_var
out_var = 'Inner Var'
return out_var
print('before: {} '.format(out_var))
inner_func()
print('after: {} '.format(out_var))
out_func()
before: Out Var
after: Inner Var
2.循環(huán)的問(wèn)題
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
f1()
9
f2()
9
f3()
9
返回的函數(shù)并沒(méi)有立刻執(zhí)行,而是直到調(diào)用了f()才執(zhí)行,在上面的例子中,每次循環(huán),都創(chuàng)建了一個(gè)新的函數(shù),然后,把創(chuàng)建的3個(gè)函數(shù)都返回了
返回閉包時(shí)牢記的一點(diǎn)就是:返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變量。
修改上述的函數(shù):
def count():
def f(j):
return lambda: j*j
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被執(zhí)行,因此i的當(dāng)前值被傳入f()
return fs
f1, f2, f3 = count()
f1()
1
f2()
4
f3()
9
閱讀
一步一步教你認(rèn)識(shí)Python閉包
深入淺出python閉包
廖大Python教程
Python 閉包