python閉包

作用域

在理解閉包前,先看一個(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 閉包

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 轉(zhuǎn)自: 淺顯理解 Python 閉包 閉包這個(gè)概念在 JavaScript 中討論和使用得比較多,不過(guò)在 Pyth...
    井底蛙蛙呱呱呱閱讀 1,611評(píng)論 0 1
  • 在這里談?wù)勎业臏\顯認(rèn)識(shí):要形成閉包,首先得有一個(gè)嵌套的函數(shù),即函數(shù)中定義了另一個(gè)函數(shù)。閉包則是一個(gè)集合,它包括了外...
    lintong閱讀 467評(píng)論 0 3
  • 一、python函數(shù)作用域LEGB python解釋器查找變量的原則(順序):L→E→G→BL:Local函數(shù)內(nèi)部...
    風(fēng)蕭雨霖閱讀 468評(píng)論 0 0
  • 我的微笑不是諷刺 如果我想諷刺你 我是不會(huì)笑的 獨(dú)自一人放棄光明 帶著別人的不解 一個(gè)人義無(wú)反顧的踏入黑暗 他們嘲...
    帥帥張閱讀 165評(píng)論 0 0
  • 就這麼安靜的等待吧 萬(wàn)物自由他的皈依 我們要做的只是欣賞沿途的風(fēng)景
    憨憨爹閱讀 97評(píng)論 0 0