理解python中的閉包和裝飾器

來源:互聯網
上載者:User
python中的閉包從表現形式上定義(解釋)為:如果在一個內建函式裡,對在外部範圍(但不是在全域範圍)的變數進行引用,那麼內建函式就被認為是閉包(closure)。

以下說明主要針對 python2.7,其他版本可能存在差異。

也許直接看定義並不太能明白,下面我們先來看一下什麼叫做內建函式:

def wai_hanshu(canshu_1):  def nei_hanshu(canshu_2): # 我在函數內部有定義了一個函數    return canshu_1*canshu_2  return nei_hanshu  # 我將內建函式返回出去a = wai_hanshu(123)   # 此時 canshu_1 = 123print aprint a(321)  # canshu_2 = 321

我在函數裡面有嵌套了一個函數,當我向外層函數傳遞一變數的之後,並賦值給 a ,我們發現 a 變成了一個函數對象,而我再次為這個函數對象傳參的時候,又獲得了內建函式的傳回值。我們知道,按照範圍的原則來說,我們在全域範圍是不能訪問局部範圍的。但是,這裡通過討巧的方法訪問到了內建函式。。

下面我們繼續看一個例子:

def wai_hanshu():  a = []  def nei_hanshu(canshu):    a.append(canshu)    return a  return nei_hanshua = wai_hanshu()print a(123)print a(321)

可以看出函數位於外部函數中的列表 a 竟然改變了。要知道為什麼,就要Crowdsourced Security Testing道什麼是python的命名空間,而命名空間就是範圍表現的原因,這裡我簡要說明一下。

引入命名空間的主要原因還是為了避免變數衝突,因為python中的模組眾多,模組中又有函數,類等,它們都要使用到變數。但如果每次都要注意不和其他變數名衝突,那就太麻煩了,開發人員應該專註於自己的問題,而不是考慮別人寫的程式中用到了什麼變數,所以python引入了命名空間。命名空間分為模組層,模組內又分為全域範圍和局部範圍,用一個圖來表示的話:

模組之間命名空間不同,而裡面還有全域範圍和局部範圍,局部範圍之前還能嵌套,這樣就能保證變數名不衝突了。這裡順便補充一下,可以通過 __name__ 屬性擷取命名空間的名字:

主檔案的命名空間是叫做 '__main__',而模組的命名空間就是模組名。

範圍的誕生,是因為當python在尋找一個變數的時候,首先會在當前的命名空間中尋找,如果當前命名空間中沒有,就到上一級的命名空間中找,以此類推,如果最後都沒找到,則觸發變數沒找到的異常。

我們之前一直說:全域範圍無法訪問局部範圍,而局部範圍能夠訪問全域範圍就這這個原因。而當我在局部範圍建立了一個和外面同名的變數時,python在找這個變數的時候首先會在當前範圍中找,找到了,就不繼續往上一級找了。

在早期的python版本時,局部範圍是不能訪問其他的局部範圍的,只能訪問全域的,而現在的版本都是依次向上一級找,這裡就提一下。

也就是因為這個特性,我們可以在內建函式中訪問外部函數中的變數,這也就是所謂的閉包了。

注意:這裡要做好對象之間的區分,例如:

def wai_hanshu():  a = []  def nei_hanshu(canshu):    a.append(canshu)    return a  return nei_hanshua = wai_hanshu()  # 我建立了一個對象b = wai_hanshu()  # 我又建立了一個對象print aprint bprint a(123)print b(321)

在這裡,我們雖然都是操作 wai_hanshu 中的變數,但是 a 和 b 完全是兩個對象,它們所在的記憶體空間也是不同的,所以裡面的資料也是獨立的。要注意不要搞混。

裝飾器

其實裝飾器就是在閉包的基礎上多進行了幾步,看代碼:

def zsq(func): # 裝飾函數  def nei():    print '我在傳入的函數執行之前做一些操作'    func() # 執行函數    print '我在目標函數執行後再做一些事情'  return neidef login():  # 被裝飾函數  print '我進行了登入功能'login = zsq(login)  # 我將被裝飾的函數傳入裝飾函數中,並覆蓋了原函數的入口login()   # 此時執行的就是被裝飾後的函數了

在看這段代碼的時候,要知道幾件事:

1.函數的參數傳遞的其實是引用,而不是值。

2.函數名也是一個變數,所以可以重新賦值。

3.賦值操作的時候,先執行等號右邊的。

只有明白了上面這些事之後,再結合一下代碼,應該就能明白什麼是裝飾器了。所謂裝飾器就是在閉包的基礎上傳遞了一個函數,然後覆蓋原來函數的執行入口,以後調用這個函數的時候,就可以額外實現一些功能了。裝飾器的存在主要是為了不修改原函數的代碼,也不修改其他調用這個函數的代碼,就能實現功能的拓展。

而python覺得讓你每次都進行重新命名操作實在太不方便,於是就給出了一個便利的寫法:

def zsq(func):  def nei():    print '我在傳入的函數執行之前做一些操作'    func() # 執行函數    print '我在目標函數執行後再做一些事情'  return nei@zsq  # 自動將其下面的函數作為參數傳到裝飾函數中去def login():  print '我進行了登入功能'login()

這些小便利也叫做python的文法糖,你可能在很多地方見過這個說法。

帶參數的裝飾器:

def zsq(a):  print '我是裝飾器的參數', a  def nei(func):    print '我在傳入的函數執行之前做一些操作'    func() # 執行函數    print '我在目標函數執行後再做一些事情'  return nei@zsq('123')def login():  print '我進行了登入功能'

相當於: login = zsq(123)(login) ,所以在這裡沒有調用就執行了。

裝飾器的嵌套:

這裡就不完整寫個例子了:

@deco1(deco_arg) @deco2 def func():   pass

相當於: func = deco1(deco_arg)(deco2(func))

也就是從上到下的嵌套了。

關於閉包和裝飾器就先講到這裡,以後有需要再補充。

以上這篇深入理解python中的閉包和裝飾器就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援topic.alibabacloud.com。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.