Python 中的裝飾器
一, 引用
[書] 流暢的Python
[書] Effective Python
二, 基本概念
問題1:裝飾器是什么?
解答: 嚴格來說,裝飾器只是語法糖, 裝飾器是可調(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
<a?href="http://haohuigou.com/members/decorate">@decorate</a>
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
<a?href="http://haohuigou.com/members/decorate">@decorate</a>
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
四, 標準庫中的裝飾器
問題1: 標準庫中都有哪些裝飾器?
解答: 標準庫中有多種裝飾器, 例如:裝飾方法的函數(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)省機制的那些工具,可能會無法正常運行;其實調(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
<a?href="http://haohuigou.com/members/decorate">@decorate</a>
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)的給一個對象添加一些額外的職責,就擴展功能而言,裝飾器模式比子類化更加靈活,在設(shè)計模式中,裝飾器和組件都是抽象類,為了給具體的組件添加行為,具體的裝飾器實例要包裝具體組件的實例,即,裝飾器和所裝飾的組件接口一致,對使用該組建的客戶透明,將客戶的請求轉(zhuǎn)發(fā)給該組件,并且可能在轉(zhuǎn)發(fā)前后執(zhí)行一些額外的操作,透明性使得可以遞歸嵌套多個裝飾器,從而可以添加任意多個功能
問題2: Python中的裝飾器函數(shù)和設(shè)計模式中的裝飾器模式有什么關(guān)系?
解答: ?修飾器模式和Python修飾器之間并不是一對一的等價關(guān)系, Python裝飾器函數(shù)更為強大,不僅僅可以實現(xiàn)裝飾器模式。