No.9 Python閉包和裝飾器
阿新 • • 發佈:2018-12-26
一、閉包:
閉包就是內層函式引用了外部函式的變數,然後返回內層函式的情況。閉包的特點就是我們返回的函式,引用了外部函式的區域性變數,如果我們希望按照我們所想,來正確的使用這個閉包的話,那就要確定我們引用的區域性變數在函式返回以後不能更改。
一個最簡單的閉包:
def outer(arg):
def inner():
return 'Using args:' + arg
return inner
f = outer('hello')
print f()
例如上面的例子,我們定義了一個outer()函式,同時在裡面定義了一個inner()函式。inner()函式使用了outer()中的引數arg,然後我們返回inner函式。所以總的來說,outer函式的返回值是內部定義的inner,同時inner使用了outer()中的引數。
那麼什麼叫外部的引數不能改變呢?
比如,我們定義一個函式getFunc(),這個函式返回了三個函式,分別定義1+1,2+2,3+3
def getFaunc():
L = []
for i in range(1,4):
def f():
return i+i
L.append(f)
return L
f1,f2,f3 = getFaunc()
print f1(),f2(),f3()
結果發現,最後的結果均是3+3。因為在我們新增完第三個函式的時候,i=3,內部函式使用到的外部引數改變了,所以我們的閉包沒有正確執行
更改為如下所示:
區別就是,在第二段程式碼中,我們執行了f(i)函式,這就使得相對的g中的變數i固定為當前f執行時的i,從而不會導致外部的變數改變這種情況def getFaunc(): L = [] for i in range(1,4): def f(i): def g(): return i+i return g tmp = f(i) L.append(tmp) return L f1,f2,f3 = getFaunc() print f1(),f2(),f3()
二、Python裝飾器
Python裝飾器,顧名思義就是給函式起到裝飾功能,這就意味著:首先是僅是裝飾而不會更改函式的任何程式碼,第二個就是就是增加原有函式的功能。
舉個例子,我們有一個函式是列印當天的日期:
import time
def getDate():
return time.strftime('%Y-%m-%d',time.localtime(time.time()))
print getDate()
如果我們希望在列印日期的時候,在伺服器上列印當前的時間,而又不希望更改程式碼。這時候就可以使用裝飾器
裝飾器看上去很複雜,自底向上拆成以下步驟很好理解:import time def getDate(): return time.strftime('%Y-%m-%d',time.localtime(time.time())) def getDateAndTime(f): # 裝飾器函式,將原函式作為引數傳入裝飾器函式 def newGetDate(): #在內部宣告一個新函式 print '[SERVER] Current time: '+time.strftime('%H-%M-%S',time.localtime(time.time())) #增加的新功能 return f() # 返回原函式執行 return newGetDate #返回新函式 getDate = getDateAndTime(getDate) #遮蔽掉原有函式 print getDate()
1. 首先,要遮蔽掉原有函式,將原有函式傳入裝飾器函式,獲得新函式,所以我們需要構造裝飾器函式
2. 在定義的裝飾器函式內部,我們需要定義一個新的函式,擴充套件功能,並作為裝飾器函式的返回值
3. 在新函式的內部,擴充套件新的函式。同時將原函式執行,並作為 返回值返回。
在python中使用這種方法來實現切面。同時,python提供了@這樣的語法糖,讓我們可很好的使用裝飾器,而不用每次都是使用getDate = getDateAndTime(getDate),來遮蔽原有函式
import time
def getDateAndTime(f):
def newGetDate():
print '[SERVER] Current time: '+time.strftime('%H-%M-%S',time.localtime(time.time()))
return f()
return newGetDate
@getDateAndTime
def getDate():
return time.strftime('%Y-%m-%d',time.localtime(time.time()))
print getDate()
與之前相比,我們只需要將@+裝飾器函式標註在原有函式上,就實現遮蔽原函式的作用。除此之外,python裝飾器也可以帶上引數
import time
def log(arg):
def getDateAndTime(f):
def newGetDate():
print '['+arg+']' +'Current time: '+time.strftime('%H-%M-%S',time.localtime(time.time()))
return f()
return newGetDate
return getDateAndTime
@log('SERVER')
def getDate():
return time.strftime('%Y-%m-%d',time.localtime(time.time()))
print getDate()
在原有的基礎上,多套了一層函式來接收引數。
上面程式碼可以解釋為:
getDateAndTime = log('SERVER')
getDate = getDateAndTime(getDate)
P.S. 文章不妥之處還望指正