裝飾器可以使函式執行前和執行後分別執行其他的附加功能,這種在**執行期間動態增加功能的方式,稱之為「裝飾器」(decorator),裝飾器的功能非常強大,但是理解起來有些困難,因此我盡量用最簡單的例子一步步的說明這個原理。
假設我定義了乙個函式f,想要在不改變原來函式定義的情況下,在函式執行前列印出start,函式執行後列印出end,要實現這樣乙個功能該怎麼實現?看下面如何用乙個簡單的裝飾器來實現:
# 使用@語法放在函式的定義上面 相當於執行 f=outer(f),此時f賦值成為了乙個新的outer函式,# 此時f函式就指向了outer函式的返回值inner,inner是乙個函式名,定義在oute函式裡面
# 原來的f是函式名可簡單理解為乙個變數,作為outer函式的引數傳遞進去了 此時引數func相當於f
def outer(func): # 定義乙個outer函式作為裝飾器
def inner(): # 如果執行inner()函式的話步驟如下:
print('start') # 1、首先列印了字元『start』,
r=func() # 2、執行func函式,func函式相當於def f(): print('中')
print('end') # 3、接著函式列印『end』
return r # 4、將func函式的結果返回
return inner
@outer
def f(): # f=outer(f)=innner
print('中')
f() # f()相當於inner(),執行inner函式的步驟看上面定義處的注釋
#列印結果順序為 start 中 end
在實際中,我們的裝飾器可能應用到不同的函式中去,這些函式的引數都不一樣,那麼我們怎麼實現乙個對任意引數都能實現功能的裝飾器?還記得我寫函式那篇部落格中,就寫一種可以接受任意引數的函式,下面來看看如何將其應用到裝飾器中去
#其實只要將上面一種不帶引數的裝飾器修改一下就可以了#修改也很簡單,只需將inner和func的引數改為 (*args,**kwargs)
#其他實現的過程和上面一種一樣,就不再介紹了
def outer(func):
def inner(*args,**kwargs):
print('start')
r=func(*args,**kwargs) # 這裡func(*args,**kwargs)相當於f(a,b)
print('end')
return r
return inner
@outer
def f(a,b):
print(a+b)
f(1,4) # f(1,4)相當於inner(1,4) 這裡列印的結果為 start 5 end
當乙個裝飾器不夠用的話,我們就可以用兩個裝飾器,當然理解起來也就更複雜了,當使用兩個裝飾器的話,首先將函式與內層裝飾器結合然後在與外層裝飾器相結合,要理解使用@語法的時候到底執行了什麼,是理解裝飾器的關鍵。這裡還是用最簡單的例子來進行說明。
def outer2(func2):def inner2(*args,**kwargs):
print('開始')
r=func2(*args,**kwargs)
print('結束')
return r
return inner2
def outer1(func1):
def inner1(*args,**kwargs):
print('start')
r=func1(*args,**kwargs)
print('end')
return r
return inner1
@outer2 # 這裡相當於執行了 f=outer1(f) f=outer2(f),步驟如下
@outer1 #1、f=outer1(f) f被重新賦值為outer1(1)的返回值inner1,
def f(): # 此時func1為 f():print('f 函式')
print('f 函式') #2、f=outer2(f) 類似f=outer2(inner1) f被重新賦值為outer2的返回值inner2
# 此時func2 為inner1函式 inner1裡面func1函式為原來的 f():print('f 函式')
f() # 相當於執行 outer2(inner1)()
>>開始 # 在outer函式裡面執行,首先列印 『開始 』
>>start # 執行func2 即執行inner1函式 列印 『start』
>>f 函式 # 在inner1函式裡面執行 func1 即f()函式,列印 『f 函式』
>>end # f函式執行完,接著執行inner1函式裡面的 print('end')
>>結束 # 最後執行inner2函式裡面的 print('結束')
前面的裝飾器本身沒有帶引數,如果要寫乙個帶引數的裝飾器怎麼辦,那麼我們就需要寫乙個三層的裝飾器,而且前面寫的裝飾器都不太規範,下面來寫乙個比較規範帶引數的裝飾器,下面來看一下**,大家可以將下面的**自我執行一下
import functoolsdef log(k=''): #這裡引數定義的是乙個預設引數,如果沒有傳入引數,預設為空,可以換成其他型別的引數
def decorator(func):
@functools.wraps(func) #這一句的功能是使被裝飾器裝飾的函式的函式名不被改變,
print('start')
print('{}:{}'.format(k, func.__name__)) #這裡使用了裝飾器的引數k
r = func(*args, **kwargs)
print('end')
return r
return decorator
@log() # fun1=log()(fun1) 裝飾器沒有使用引數
def fun1(a):
print(a + 10)
fun1(10)
@log('excute') # fun2=log('excute')(fun2) 裝飾器使用給定引數
def fun2(a):
print(a + 20)
fun2(10)
python全棧開發 day12 裝飾器
一 裝飾器 1 什麼是裝飾器 裝飾器的本質就是利用閉包,在滿足開放 修改函式鎖包含的功能 封閉 不改變源 的情況下完成操作。2 裝飾器的基本運用 def name judge f defa name,password if len name 3 print 賬號驗證成功 res f name,pas...
Python全棧開發之MySQL
smartypants將ascii標點字元轉換為 智慧型 印刷標點html實體。例如 type ascii html single backticks isn t this fun?isn t this fun?quotes isn t this fun?isn t this fun?dashes ...
python全棧學習 裝飾器與檔案練習
驗證賬號密碼,從檔案中選取賬號密碼進行驗證 利用裝飾器知識 步驟 1 先搭建裝飾器模板 inter name input please input your name inter pwd input please input your password def checking func def w...