Python中的閉包

來源:互聯網
上載者:User

標籤:

閉包(closure)是函數式編程的重要的文法結構,Python也支援這一特性,下面就開始介紹Python中的閉包。

首先看看閉包的概念:閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變數的函數。這個被引用的自由變數將和這個函數一同存在,即使已經離開了創造它的環境也不例外。所以,閉包是由函數和與其相關的引用環境組合而成的實體。

在開始介紹閉包之前先看看Python中的namespace。

Python中的namespace

Python中通過提供 namespace 來實現重名函數/方法、變數等資訊的識別,其一共有三種 namespace,分別為:

  • local namespace: 作用範圍為當前函數或者類方法
  • global namespace: 作用範圍為當前模組
  • build-in namespace: 作用範圍為所有模組

當函數/方法、變數等資訊發生重名時,Python會按照 "local namespace -> global namespace -> build-in namespace"的順序搜尋使用者所需元素,並且以第一個找到此元素的 namespace 為準。

同時,Python中的內建函數locals()和globals()可以用來查看不同namespace中定義的元素。

看一個例子:

s = "string in global"num = 99def numFunc(a, b):    num = 100    print "print s in numFunc: ", s        def addFunc(a, b):        s = "string in addFunc"        print "print s in addFunc: ", s        print "print num in addFunc: ", num        print "locals of addFunc: ", locals()        print         return "%d + %d = %d" %(a, b, a + b)            print "locals of numFunc: ", locals()    print     return addFunc(a, b)        numFunc(3, 6)    print "globals: ", globals()

上面代碼的結果為:

代碼中通過locals()和globals()擷取了不同namespace中定義的元素,當在"numFunc"函數中訪問s變數的時候,由於local namespace中沒有定義s,所以會找到global中的s;但是,由於"addFunc"的local namespace中有s,所以可以直接使用。

Python建立閉包

下面通過一個例子一步一步分析一下Python如何?閉包。

一個閉包的例子

下面看一個閉包的簡單的例子,例子中定義了一個可以配置打招呼的函數"greeting_conf",在這個函數中,內嵌了一個"greeting"的內嵌函數:

def greeting_conf(prefix):    def greeting(name):        print prefix, name    return greeting    mGreeting = greeting_conf("Good Morning")mGreeting("Wilber")mGreeting("Will")printaGreeting = greeting_conf("Good Afternoon")    aGreeting("Wilber")aGreeting("Will")

代碼的結果為:

上面的代碼有下面幾個注意點:

  1. "greeting"函數訪問了non-local的變數"prefix",根據前面namespace的介紹,這點是完全合法的
  2. 變數"prefix"並沒有隨著函數的退出而銷毀,反而是生命週期得到了延長

下面看看為什麼變數"prefix"生命週期得到了延長?

__closure__屬性

在Python中,函數對象有一個__closure__屬性,我們可以通過這個屬性看看閉包的一些細節。

def greeting_conf(prefix):    def greeting(name):        print prefix, name    return greeting    mGreeting = greeting_conf("Good Morning")      print dir(mGreeting)print mGreeting.__closure__print type(mGreeting.__closure__[0])print mGreeting.__closure__[0].cell_contents

通過__closure__屬性看到,它對應了一個tuple,tuple的內部包含了cell類型的對象。

對於這個例子,可以得到cell的值(內容)為"Good Morning",也就是變數"prefix"的值。

從這裡可以看到閉包的原理,當內嵌函數引用了包含它的函數(enclosing function)中的變數後,這些變數會被儲存在enclosing function的__closure__屬性中,成為enclosing function本身的一部分;也就是說,這些變數的生命週期會和enclosing function一樣。

閉包和函數

閉包只是在表現形式上跟函數類似,但實際上不是函數。

def greeting_conf(prefix):    def greeting(name):        print prefix, name    return greeting    mGreeting = greeting_conf("Good Morning")      print "function name is:", mGreeting.__name__print "id of mGreeting is:", id(mGreeting)printaGreeting = greeting_conf("Good Afternoon")  print "function name is:", aGreeting.__name__print "id of aGreeting is:", id(aGreeting)

從代碼的結果中可以看到,閉包在運行時可以有多個執行個體,不同的引用環境(這裡就是prefix變數)和相同的函數(這裡就是greeting)組合可以產生不同的執行個體。

Python中怎麼建立閉包

在Python中建立一個閉包可以歸結為以下三點:

  • 閉包函數必須有內嵌函數
  • 內嵌函數需要引用該嵌套函數上一級namespace中的變數
  • 閉包函數必須返回內嵌函數

通過這三點,就可以建立一個閉包,是不是想到了上一篇中介紹的Python裝飾器。沒錯,Python裝飾器就是使用了閉包。

總結

本文介紹了如何通過Python建立一個閉包,以及Python建立的閉包是如何工作的。

Python中的閉包

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.