閉包 裝飾器

2022-09-19 05:06:13 字數 4188 閱讀 1300

python 閉包,裝飾器

一 閉包

如果在乙個函式的內部定義了另乙個函式,外部的函式叫它外函式,內部的函式叫它內函式。

1、閉包條件

1、在乙個外函式中定義了乙個內函式。

2、內函式裡運用了外函式的臨時變數。

3、並且外函式的返回值是內函式的引用(就是函式名,函式名就是該函式**和引數儲存的位址,和 常量定義a = 5完全等價)

一般情況下,如果乙個函式結束,函式的內部所有東西都會釋放掉,還給記憶體,區域性變數都會消失

但是閉包是一種特殊情況,如果外函式在結束的時候發現有自己的臨時變數將來會在內部函式中用到,就把這個臨時變數繫結給了內部函式,然後自己再結束。

#

閉包函式的例項

#outer是外部函式 a和b都是外函式的臨時變數

defouter( a ):

b = 10

#inner是內函式

definner():

#在內函式中 用到了外函式的臨時變數

print(a+b)

#外函式的返回值是內函式的引用

return

inner

if__name__ == '

__main__':

#在這裡我們呼叫外函式傳入引數5

#此時外函式兩個臨時變數 a是5 b是10 ,並建立了內函式,然後把內函式的引用返回存給了demo

#外函式結束的時候發現內部函式將會用到自己的臨時變數,這兩個臨時變數就不會釋放,會繫結給這個內部函式

demo = outer(5)

#我們呼叫內部函式,看一看內部函式是不是能使用外部函式的臨時變數

#demo存了外函式的返回值,也就是inner函式的引用,這裡相當於執行inner函式

demo() #

15demo2 = outer(7)

demo2()

#17

上面例子是乙個最簡單的很典型的閉包。

a、外函式返回了內函式的引用

引用是什麼?在python中一切都是物件,包括整型資料1,1.23,函式,都是物件。

當我們進行a=1的時候,實際上在記憶體當中有乙個地方存了值1,然後用a這個變數名存了1所在記憶體位置的引用。引用就類似於c語言裡的指標,引用也可以理解成位址。a只不過是乙個變數名字,a裡面存的是1這個數值所在的位址,就是a裡面存了數值1的引用。

相同的道理,在python中定義乙個函式def demo(): 的時候,記憶體當中會開闢一些空間,存下這個函式的**、內部的區域性變數等等。這個demo只不過是乙個變數名字,它裡面存了這個函式所在位置的引用而已。我們還可以進行x = demo, y = demo, 這樣的操作就相當於,把demo裡存的東西賦值給x和y,這樣x 和y 都指向了demo函式所在的引用,在這之後我們可以用x() 或者 y() 來呼叫我們自己建立的demo() ,呼叫的實際上根本就是乙個函式,x、y和demo三個變數名存了同乙個函式的引用。

返回內函式,對於閉包,在外函式outer中 最後return inner,在呼叫外函式 demo = outer() 的時候,outer返回了inner,inner是乙個函式的引用,這個引用被存入了demo中。所以接下來再進行使用demo() 的時候,相當於使用了inner函式

乙個函式,如果函式名後緊跟一對括號,說明現在就要呼叫這個函式,如果不跟括號,只是乙個函式的名字,裡面存了函式所在位置的引用。

b、外函式把臨時變數繫結給內函式

乙個函式結束的時候,會把自己的臨時變數都釋放給記憶體,之後變數都不存在了。一般情況下,確實是這樣的。但是閉包是乙個特別的情況。外部函式發現,自己的臨時變數會在將來的內部函式中用到,自己在結束的時候,返回內函式的同時,會把外函式的臨時變數和內函式繫結在一起。所以外函式已經結束了,呼叫內函式的時候仍然能夠使用外函式的臨時變數。

在以上實際例子中,兩次呼叫外部函式outer,分別傳入的值是5和7。內部函式只定義了一次,我們發現呼叫的時候,內部函式是能識別外函式的臨時變數是不一樣的。python中一切都是物件,雖然函式我們只定義了一次,但是外函式在執行的時候,實際上是按照裡面**執行的,外函式裡建立了乙個函式,我們每次呼叫外函式,它都建立乙個內函式,雖然**一樣,但是卻建立了不同的物件,並且把每次傳入的臨時變數數值繫結給內函式,再把內函式引用返回。雖然內函式**是一樣的,但其實,我們每次呼叫外函式,都返回不同的例項物件的引用,他們的功能是一樣的,但是它們實際上不是同乙個函式物件。

2、閉包中內函式修改外函式區域性變數

在閉包內函式中,可以隨意使用外函式繫結來的臨時變數,但是如果想修改外函式臨時變數數值的時候發現出問題了!

在基本的python語法當中,乙個函式可以隨意讀取全域性資料,但是要修改全域性資料的時候有兩種方法:

1、global 宣告全域性變數

2、全域性變數是可變型別資料的時候可以修改

在閉包內函式也是類似的情況。在內函式中想修改閉包變數(外函式繫結給內函式的區域性變數)的時候:

1、在python3中,可以用nonlocal 關鍵字宣告 乙個變數, 表示這個變數不是區域性變數空間的變數,需要向上一層變數空間找這個變數

2、在python2中,沒有nonlocal這個關鍵字,可以把閉包變數改成可變型別資料進行修改,比如列表。

#修改閉包變數的例項

# outer是外部函式 a和b都是外函式的臨時變數

def outer01( a ):

b = 10 # a和b都是閉包變數

c = [a] #這裡對應修改閉包變數的方法2

# inner是內函式

def inner():

#內函式中想修改閉包變數

# 方法1 nonlocal關鍵字宣告

nonlocal b

b+=1

# 方法二,把閉包變數修改成可變資料型別 比如列表

c[0] += 1

print(c[0])

print(b)

# 外函式的返回值是內函式的引用

return inner

if __name__ == '__main__':

demo = outer(5)

demo() # 6 11

從上面**中能看出來,在內函式中,分別對閉包變數進行了修改,列印出來的結果也確實是修改之後的結果。以上兩種方法就是內函式修改閉包變數的方法。

還有一點需要注意:使用閉包的過程中,一旦外函式被呼叫一次返回了內函式的引用,雖然每次呼叫內函式,是開啟乙個函式執行過後消亡,但是閉包變數實際上只有乙份,每次開啟內函式都在使用同乙份閉包變數

#coding:utf8

# 閉包變數為外函式outer的引數變數

def outer(x):

def inner(y):

nonlocal x

x += y

return x

return inner

a = outer(10) # 其實很容易理解: a指向outer(10),只要a沒有改變,自然都是指向 outer(10)

print(a(1)) # 11 ----> 這裡 y = 1 ;內部函式inner的引數賦值改變不會影響外部函式的引數變數

print(a(3)) # 14 ----> 這裡 y = 3

兩次分別列印出11和14,由此可見,每次呼叫inner的時候,使用的閉包變數x實際上是同乙個。

閉包用途

3.1 裝飾器!裝飾器是做什麼的?其中乙個應用就是,我們工作中寫了乙個登入功能,我們想統計這個功能執行花了多長時間,我們可以用裝飾器裝飾這個登入模組,裝飾器幫我們完成登入函式執行之前和之後取時間。

3.2 物件導向!經歷了上面的分析,我們發現外函式的臨時變數送給了內函式。大家回想一下類物件的情況,物件有好多類似的屬性和方法,所以我們建立類,用類建立出來的物件都具有相同的屬性方法。閉包也是實現物件導向的方法之一。在python當中雖然我們不這樣用,在其他程式語言入比如**ascript中,經常用閉包來實現物件導向程式設計

3.3 實現單利模式!

閉包 裝飾器

外部函式返回內部函式的引用 內部函式可以直接使用外部函式的環境變數 語法 外部函式通過返回內部函式的引用 內部函式可以直接使用外部函式的 環境變數 自由變數 函式執行時間統計 執行函式前預備處理 執行函式後清理功能 許可權校驗等場景 快取有且只有乙個引數 指向了被裝飾的函式的引用 使用裝飾器 裝飾器...

閉包,裝飾器

多層函式巢狀,函式裡面還有定義函式,一般是兩個 往往內層函式會用到外層函式的變數,把內層函式以及外部函式的變數當成乙個特殊的物件,這就是閉包。閉包比物件導向更純淨 更輕量,既有資料又有執行資料的 比普通函式功能更強大,不僅有 還有資料 利用閉包的基本原理,對乙個目標函式進行裝飾,即在執行乙個目標函式...

閉包 裝飾器

定義乙個函式 def test number 在函式內部再定義乙個函式,並且這個函式用到了外邊函式的變數,那麼將這個函式及用到的一些變數稱之為閉包 def test in number in print in text in 函式,number in is d number in return nu...