Python全棧開發之8 裝飾器詳解

2022-03-27 13:32:22 字數 3688 閱讀 4933

裝飾器可以使函式執行前和執行後分別執行其他的附加功能,這種在**執行期間動態增加功能的方式,稱之為「裝飾器」(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 functools

def 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...