一, 引用
二, 基本概念
問題1:裝飾器是什么?
解答: 嚴(yán)格來說,裝飾器只是語法糖, 裝飾器是可調(diào)用的對象,可以像常規(guī)的可調(diào)用對象那樣調(diào)用,特殊的地方是裝飾器的參數(shù)是一個函數(shù)
問題2:裝飾器有什么特性?
解答: 裝飾器有2個特性,一是可以把被裝飾的函數(shù)替換成其他函數(shù), 二是可以在加載模塊時候立即執(zhí)行
def decorate(func):
print('running decorate', func)
def decorate_inner():
print('running decorate_inner function')
return func()
return decorate_inner
@decorate
def func_1():
print('running func_1')
if __name__ == '__main__':
print(func_1)
#返回值
running decorate <function func_1 at 0x7f29f644d268>
<function decorate.<locals>.decorate_inner at 0x7f29f641cb70>
問題3:如何使用被裝飾函數(shù)中的參數(shù)?
解答: 通過*args
和 **kwargs
傳遞被修飾函數(shù)中的參數(shù)
def decorate(func):
def decorate_inner(*args, **kwargs):
print(type(args), type(kwargs))
print('args', args, 'kwargs', kwargs)
return func(*args, **kwargs)
return decorate_inner
@decorate
def func_1(*args, **kwargs):
print(args, kwargs)
if __name__ == '__main__':
func_1('1', '2', '3', para_1='1', para_2='2', para_3='3')
#返回值
<class 'tuple'> <class 'dict'>
args ('1', '2', '3') kwargs {'para_2': '2', 'para_1': '1', 'para_3': '3'}
('1', '2', '3') {'para_2': '2', 'para_1': '1', 'para_3': '3'}
三, 疊放裝飾器
問題1:疊放裝飾器執(zhí)行順序是什么?
解答: 如果一個函數(shù)被多個裝飾器修飾,其實應(yīng)該是該函數(shù)先被最里面的裝飾器修飾后(下面例子中函數(shù)main()先被inner裝飾,變成新的函數(shù)),變成另一個函數(shù)后,再次被裝飾器修飾
def outer(func):
print('enter outer', func)
def wrapper():
print('running outer')
func()
return wrapper
def inner(func):
print('enter inner', func)
def wrapper():
print('running inner')
func()
return wrapper
@outer
@inner
def main():
print('running main')
if __name__ == '__main__':
main()
#返回值
enter inner <function main at 0x7fa1c96e8b70>
enter outer <function inner.<locals>.wrapper at 0x7fa1c96e8bf8>
running outer
running inner
running main
四, 標(biāo)準(zhǔn)庫中的裝飾器
問題1: 標(biāo)準(zhǔn)庫中都有哪些裝飾器?
解答: 標(biāo)準(zhǔn)庫中有多種裝飾器, 例如:裝飾方法的函數(shù)有property, classmethod, staticmethod; functools模塊中的lru_cache, singledispatch, wraps 等等
from functools import lru_cache
from functools import singledispatch
from functools import wraps
問題2:為什么要使用@wraps裝飾器?它的作用是什么?
解答: 使用裝飾器會產(chǎn)生我們可能不希望出現(xiàn)的副作用, 例如:改變被修飾函數(shù)名稱,對于調(diào)試器或者對象序列化器等需要使用內(nèi)省機(jī)制的那些工具,可能會無法正常運行;其實調(diào)用裝飾器后,會將同一個作用域中原來函數(shù)同名的那個變量(例如下面的func_1),重新賦值為裝飾器返回的對象;使用@wraps后,會把與內(nèi)部函數(shù)(被修飾函數(shù),例如下面的func_1)相關(guān)的重要元數(shù)據(jù)全部復(fù)制到外圍函數(shù)(例如下面的decorate_inner)
from functools import wraps
def decorate(func):
print('running decorate', func)
@wraps(func)
def decorate_inner():
print('running decorate_inner function', decorate_inner)
return func()
return decorate_inner
@decorate
def func_1():
print('running func_1', func_1)
if __name__ == '__main__':
func_1()
#返回值
running decorate <function func_1 at 0x7f145d2c2268>
running decorate_inner function <function func_1 at 0x7f145b9731e0>
running func_1 <function func_1 at 0x7f145b9731e0>
五, 裝飾器設(shè)計模式
問題1: 什么是裝飾器設(shè)計模式?
解答: 動態(tài)的給一個對象添加一些額外的職責(zé),就擴(kuò)展功能而言,裝飾器模式比子類化更加靈活,在設(shè)計模式中,裝飾器和組件都是抽象類,為了給具體的組件添加行為,具體的裝飾器實例要包裝具體組件的實例,即,裝飾器和所裝飾的組件接口一致,對使用該組建的客戶透明,將客戶的請求轉(zhuǎn)發(fā)給該組件,并且可能在轉(zhuǎn)發(fā)前后執(zhí)行一些額外的操作,透明性使得可以遞歸嵌套多個裝飾器,從而可以添加任意多個功能
問題2: Python中的裝飾器函數(shù)和設(shè)計模式中的裝飾器模式有什么關(guān)系?
解答: 修飾器模式和Python修飾器之間并不是一對一的等價關(guān)系, Python裝飾器函數(shù)更為強(qiáng)大,不僅僅可以實現(xiàn)裝飾器模式。