Python3 裝飾器

2024-08-14 18:31 更新

什么是裝飾器

裝飾器(decorators)是 一種設計模式,之所以被稱為裝飾器,是因為其本質是對一個函數或者對象進行了"修飾",裝飾器可以在你進入對象或者函數前后進行一些操作,可以在不修改原來的代碼的情況下讓函數的行為發(fā)生改變。

面向切面編程(AOP),是指某些業(yè)務流程具有相似性,只有部分核心代碼有差異,我們可以把相同部分的代碼提取出來,不同的代碼(切片)另外實現,這就是面向切面編程。在 java 的 spring 中,這是一種很重要的技術,而 python 也有類似的實現方式,最常見的就是裝飾器。


裝飾器的功能

裝飾器允許你動態(tài)地修改函數或類的行為。它本質上是一個函數,可以接受一個函數作為參數,并返回一個新的函數或者修改原來的函數。


裝飾器語法

裝飾器雖然是一個函數,但它更常見的使用方式是使用 ?@decorator_name? 來應用在函數或方法上。

例如:

from flask import Flask
app =Flask(__name__)
?
# app.route就是一個Flask內置的裝飾器
@app.route("index")
def index():
   return "hello"

Python 還提供了一些內置的裝飾器,比如 ?@staticmethod? 和 ?@classmethod?,用于定義靜態(tài)方法和類方法。


裝飾器的應用場景:

  • 日志記錄: 裝飾器可用于記錄函數的調用信息、參數和返回值。
  • 性能分析: 可以使用裝飾器來測量函數的執(zhí)行時間。
  • 權限控制: 裝飾器可用于限制對某些函數的訪問權限。
  • 緩存: 裝飾器可用于實現函數結果的緩存,以提高性能。


簡單案例

import time
def runtime(func):#定義一個裝飾器函數叫runtime 他接受一個函數func作為參數
 def wrapper(*args, **kwargs): # 包裝函數wrapper,這里接受所有參數
   
   # 這里是在調用原始函數前添加的新功能
   starttime= time.time() #獲取開始執(zhí)行的時間
   
   # 在包裝函數中調用原始函數(可以不調用)
   result = func(*args, **kwargs) # 函數傳遞的參數為包裝函數接受的所有參數
   
  # 這里是在調用原始函數后添加的新功能
   endtime= time.time() #獲取結束執(zhí)行的時間
?
   print(f"函數{func.__name__}運行時間:{endtime-starttime}秒") #輸出函數運行的時間
?
   return result # 返回函數運行的結果
 return wrapper
?
# 使用裝飾器裝飾一個函數,現在運行這個函數時,會運行裝飾器的代碼,然后再由裝飾器運行這個func
# (如果裝飾器不調用這個函數,而是調用別的函數,就可以替換掉原有函數的功能)
@runtime
def sleepfunc(arg1, arg2):
 time.sleep(2)
 return arg1 + arg2
?
print(sleepfunc(1, 2))
函數sleepfunc運行時間:2.005006790161133秒
3

解析:?runtime?是一個裝飾器函數,它接受一個函數 ?func? 作為參數,并返回一個內部函數 ?wrapper?,在 ?wrapper? 函數內部,你可以執(zhí)行一些額外的操作,然后調用原始函數 ?func?,并返回其結果。

  • runtime 是裝飾器,它接收一個函數 func 作為參數。
  • wrapper 是內部函數,它是實際會被調用的新函數,它包裹了原始函數的調用,并在其前后增加了額外的行為。
  • 當我們使用 ?@runtime? 前綴在 ?sleepfunc? 定義前,Python會自動將 ?sleepfunc? 作為參數傳遞給 ?runtime?,然后將返回的 ?wrapper? 函數替換掉原來的 ?sleepfunc?。


使用裝飾器

裝飾器通過 ?@? 符號應用在函數定義之前,例如:

@time_logger
def target_function():
  pass

等同于:

def target_function():
  pass
target_function = time_logger(target_function)

這會將 ?target_function? 函數傳遞給 ?decorator? 裝飾器,并將返回的函數重新賦值給 ?target_function?。從而,每次調用 ?target_function? 時,實際上是調用了經過裝飾器處理后的函數。

通過裝飾器,開發(fā)者可以在保持代碼整潔的同時,靈活且高效地擴展程序的功能。


帶參數的裝飾器

裝飾器函數也可以接受參數,但是需要在原有的裝飾器外再套一層裝飾器。

例如:

實例

import time
?
?
?
def runtime(n:int):#定義一個裝飾裝飾器的裝飾函數,他可以接受參數
 def decorator(func):#定義一個裝飾器函數,接受一個函數func作為參數
   def wrapper(*args, **kwargs): # 包裝函數wrapper,這里接受所有參數
   
       # 這里是在調用原始函數前添加的新功能
       starttime= time.time() #獲取開始執(zhí)行的時間
   
       # 在包裝函數中調用原始函數(可以不調用)
       result = func(*args, **kwargs) # 函數傳遞的參數為包裝函數接受的所有參數
   
       # 這里是在調用原始函數后添加的新功能
       endtime= time.time() #獲取結束執(zhí)行的時間
?
       print(f"函數{func.__name__}運行時間:{endtime-starttime}秒") #輸出函數運行的時間
       print("裝飾器傳遞過來的參數為:",n)
?
       return result # 返回函數運行的結果
   return wrapper # 返回包裝函數
 return decorator # 返回裝飾器函數
?
?
# 使用裝飾器裝飾一個函數,現在運行這個函數時,會運行裝飾器的代碼,然后再由裝飾器運行這個func
# (如果裝飾器不調用這個函數,而是調用別的函數,就可以替換掉原有函數的功能)
@runtime(1)
def sleepfunc(arg1, arg2):
 time.sleep(2)
 return arg1 + arg2
?
print(sleepfunc(1, 2))

運行結果為:

函數sleepfunc運行時間:2.000138521194458秒
裝飾器傳遞過來的參數為: 1
3

以上代碼中 ?runtime?函數是一個帶參數的裝飾器,它接受一個整數參數 ?n?,然后返回一個裝飾器函數。該裝飾器函數內部定義了 ?wrapper? 函數,我們可以在裝飾器內部獲取到這個參數n(內層函數可以獲取到外層函數的變量),上面只是簡單的打印了這個參數,我們也可以讓這個參數參與到代碼運行中。

比如?flask?的?@app.route?就可以接受參數來決定該方法是?get?還是?post?。


類裝飾器

除了函數裝飾器,Python 還支持類裝飾器。類裝飾器是包含 ?call? 方法的類,它接受一個函數作為參數,并返回一個新的函數。

實例

class DecoratorClass:
 def __init__(self, func):
   self.func = func
?
 def __call__(self, *args, **kwargs):
   # 在調用原始函數之前/之后執(zhí)行的代碼
   result = self.func(*args, **kwargs)
   # 在調用原始函數之后執(zhí)行的代碼
   return result
   
@DecoratorClass
def my_function():
   pass
以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號