W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
裝飾器(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)方法和類方法。
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
? 前綴在 ?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ù),但是需要在原有的裝飾器外再套一層裝飾器。
例如:
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
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: