Python3 裝飾器

2024-08-14 18:31 更新

什么是裝飾器

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

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


裝飾器的功能

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


裝飾器語法

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

例如:

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

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


裝飾器的應(yīng)用場景:

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


簡單案例

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

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

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


使用裝飾器

裝飾器通過 ?@? 符號應(yīng)用在函數(shù)定義之前,例如:

@time_logger
def target_function():
  pass

等同于:

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

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

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


帶參數(shù)的裝飾器

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

例如:

實例

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

運行結(jié)果為:

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

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

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


類裝飾器

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

實例

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號