Python3 閉包及裝飾器

2021-10-02 20:56:13 字數 4705 閱讀 5166

一.什麼是閉包

不同程式語言實現閉包的方式是不同的,python中閉包從表現形式上看,如果在乙個內部函式裡,對在外部作用域(但不是在全域性作用域)的變數進行引用,那麼內部函式就被認為是閉包(closure)。

舉個例子:

def outer(x):

def inner(y):

return x + y

return inner

結合這段簡單的**和定義來說明閉包:

inner(y)就是這個內部函式,對在外部作用域(但不是在全域性作用域)的變數進行引用:x就是被引用的變數,x在外部作用域outer裡面,但不在全域性作用域裡,則這個內部函式inner就是乙個閉包。

再稍微講究一點的解釋是,閉包=函式塊+定義函式時的環境,inner就是函式塊,x就是環境,當然這個環境可以有很多,不止乙個簡單的x。

在函式outer中定義了乙個inner函式,inner函式訪問外部函式outer的(引數)變數,並且把inner函式作為返回值返回給outer函式。

二:閉包的作用

閉包也就是裝飾器的實現,其作用在於:

1、不改變原函式內**段;

2、不改變原函式名稱;

3、為原函式增加新的功能,改變輸出結果等等。

搜尋變數名的作用域優先順序:l區域性 > e巢狀 > g全域性 > b內建

l區域性(local):def構建函式段中,定義的變數

e巢狀(enclosing):當前域該變數名無賦值操作,引用父級;反之引用當前

g全域性(global):不在任何函式段內,寫在所有函式外邊,最外層的變數

b內建(built-in):系統__builtin__模組中的變數

l和g不用說了,區域性和全域性,就是函式裡和函式外。

b內建,也不提了,一般碰不到。

主要說說e

def fa():

a = 0

def fb():

print(a)

fb()

fa()

fa()巢狀fb(),子函式中沒有對變數a進行賦值操作,也是a=***之類的。所以在fb()的l中沒有的時候,可以向上層也就是父級尋找,使用e作用域的中的變數。

假設這裡改了一句

def fa():

a = 0

def fb():

print(a)

a = 1 # 這裡加一句對a的賦值

fb()

fa()

# 輸出結果

traceback (most recent call last):

file "test.py", line 5, in fb

print(a)

unboundlocalerror: local variable 'a' referenced before assignment

你會發現在print(a)的時候,就出現了a未定義的錯誤。原因在於fb()的l區域性作用域中,有對a進行了賦值這樣的操作,a不能使用父級的定義。而在列印輸出時,區域性函式中a尚未定義,導致程式報錯。

那麼區域性就不可以修改父級了麼?其實是可以的。

原理和global宣告變數,區域性變全域性是乙個道理。

def fa():

a = 0

def fb():

nonlocal a

print(a)

a = 1

fb()

fa()

使用nonlocal宣告後,a的作用域就擴充套件到了父級,就又可以使用e巢狀作用域了。

2、閉包說明

首先以一段閉包實現的**為例:

def fa():

a = 0

def fb():

nonlocal a

print(a)

a = 1

return fb

f = fa()

f()f()

# 輸出結果

01

將閉包函式,之前的巢狀函式對比

發現**基本一致,只有最後的fb()變成了return fb

def fa():                # def fa():

a = 0                #     a = 0

def fb():            #     def fb():

nonlocal a        #        nonlocal a

print(a)        #        print(a)

a = 1            #        a = 1

return fb            #    fb()

這就導致程式沒有執行內部函式fb,而是將內部函式打包作為輸出。(閉包執行流程可以看當前頁最下方的測試記錄)

所以通過f = fa()返回的函式物件f,其實執行的是,打包好的內部函式fb()的內容,就是下面這段

def fb():

nonlocal a

print(a)

a = 1

但它的特別之處在於:

f建立時所攜帶的父層變數a會一直存在於記憶體中,不會因為函式執行完成而消失(除非你主動del f刪掉)

f = fa()

f()f()

# 輸出結果

01

其結果分別為0和1的原因在於:

第1次f(),讀取了f初始化時的a值進行輸出為0,然後通過a=1,a才變成1

第2次f(),因為a已經變成1,所以a值輸出為1,然後通過a=1,a依舊是1

其建立特徵在於:函式巢狀,返回函式物件,返回那個函式引用了父層變數。

其使用特徵在於:儲存區域性資訊不被銷毀。

三、裝飾器說明

說完了閉包,來說裝飾器。

首先寫乙個閉包程式,

函式funca將輸入引數+1,作為結果輸出。

閉包實現的是任意輸入函式,將其輸出結果求平方。

所以將funca打包成new_funca後,輸出結果變成了(1+1)2=4

def build_bibao(f):

def bibao(*args):

result = f(*args)

return result**2

return bibao

def funca(num):

return num+1

new_funca = build_bibao(funca)

print(new_funca(1))

# 輸出結果

4

那麼將上面**用@簡寫,其實就是裝飾器的實現。

def build_bibao(f):

def bibao(*args):

result = f(*args)

return result**2

return bibao

@build_bibao

def funca(num):

return num+1

print(funca(1))

# 輸出結果

4

裝飾器實際上對輸入函式funca做了包裝,然後給你返回了包裝好的,附加了新功能(結果求平方)的funca

下面是自己研究閉包時候,做的一些測試(只做記錄,可以忽略)

**如下:

def funx(x):

return x*x

def build_bibao(func):

def bibao(j):

return func(j)

return bibao

new_funx = build_bibao(funx)

new_funx(5)

新增列印輸出,確認其執行過程:

def funx(x):

return x*x

def build_bibao(func):

print(0,type(func),func)

def bibao(j):

print(3,type(j),j)

print(4,type(func.__name__),func.__name__)

print(5,type(func(j)),func(j))

return func(j)

print(1,type(bibao),bibao)

return bibao

new_funx = build_bibao(funx)

print(2,type(new_funx),new_funx)

new_funx(5)

可以依照列印序號,確認執行順序和結果

0 1 .bibao at 0x000002aa4edd8730>

2 .bibao at 0x000002aa4edd8730>

3 54 funx

5 25

[finished in 0.2s]

Python閉包及裝飾器

先看乙個例子 def outer x definner y return x y return innder add outer 8 print add 6 我們定義了乙個方法outer,方法內部又定義了乙個方法inner,方法outer返回值為內部定義的方法inner。同時,內部方法innder使...

Python 閉包及裝飾器

閉包是指延伸了作用域的函式。自由變數 free variable 指未在本地作用域中繫結的變數 函式裝飾器用於在原始碼中標記函式,以某種方式增強函式的行為。裝飾器實質,把被裝飾的函式替換為新函式,二者接收相同的引數,繫結了被裝飾函式最為自由變數,返回被裝飾函式本該返回的值,同時還會做些額外操作 裝飾...

Python3 閉包 裝飾器

首先要理解在python中一切皆物件,函式也是物件。多層函式巢狀,函式裡面還有定義函式,一般是兩個 往往內層函式會用到外層函式的變數,把內層函式以及外部函式的變數當成乙個特殊的物件,這就是閉包。閉包比物件導向更純淨 更輕量,既有資料又有執行資料的 比普通函式功能更強大,不僅有 還有資料。def fu...