裝飾器模式:允許向一個現有的對象添加新的功能,同時又不改變其結構。在python中,裝飾器可以是一個函數或者一個類。
在解釋裝飾器模式時,先了解下python中@符号的含義:
1、@符号是python中的一種修飾符,用來修飾函數,修飾符必須出現在函數定義的前一行,不允許和函數定義在同一行。
2、一個修飾符就是一個函數,它将修飾的函數作為參數,并傳回修飾後的同名函數或者其他可以調用的類型。
書面解釋比較抽象,來看一個例子:
import time
def get_current_time(func):
print time.time()
return func()
@get_current_time
def sum():
print "sum 函數求和"
運作結果:
1531305351.73
sum 函數求和
其中@符号修飾了sum函數,相當于将sum函數作為參數傳入了get_current_time函數,然後執行了get_current_time函數的主體流程。
python中裝飾器模式應用分為:
1、函數裝飾器,可以了解為裝飾器為某個定義的函數。
2、帶參數的裝飾器,可以了解為裝飾器函數可以包含參數傳遞。
3、類裝飾器,可以了解為裝飾器為一個定義的類,這種情況一般應用較少。
python内置裝飾器有@staticmathod、@classmethod、@property,分别對應靜态方法,類方法,類屬性。
先看函數裝飾器:
import logging
def get_logging(func):
def wrapper(*args, **kwargs):
logging.warn("this is debug log :%s"% func.__name__)
return func(*args, **kwargs)
return wrapper
@get_logging
def test_func():
print "this is a test function"
if __name__ == '__main__':
test_func()
輸出結果為:
this is a test function
WARNING:root:this is debug log :test_func
函數get_logging就是裝飾器,它将執行真正業務函數test_func包裹在函數中,看起來test_func被get_logging裝飾了。簡單解釋下*args和**kwargs,*args作為函數參數時,是一種可變參數,可以傳一個或者多個值,如果定義函數test(*args),那麼調用test時,test(1,2,3,None,5),此時的args擷取到的值為一個元組tuple,為(1,2,3,None,5);同理**args作為函數參數時,也是一種可變參數,差別是這裡傳遞的值是以鍵值對呈現的,如果定義函數test(**args),那麼調用test時,test(a = 1,b = 2,c = 3),此時的args擷取到的值為一個字典dict,值為{'a':1,'b':2,'c':3}。
帶參數的裝飾器:
裝飾器還有更大的甯活性,比如帶參數的裝飾器。裝飾器的文法允許我們在調用時,提供其他參數,比如get_logging(flag),這樣就為裝飾器的執行提供了更大的靈活性。
比如将上面的例子稍微修改下:
import logging
def use_decorator(level):
def get_logging(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("this is warn log :%s"% func.__name__)
if level == "error":
logging.error("this is error log :%s" % func.__name__)
return func(*args, **kwargs)
return wrapper
return get_logging
@use_decorator(level = "error")
def test_func():
print "this is a test function"
if __name__ == '__main__':
test_func()
輸出結果為:
ERROR:root:this is error log :test_func
this is a test function
給原來的get_logging外層加了一個use_decorator函數,實作函數參數的傳遞與判斷。use_decorator是一個允許帶參數的裝飾器,實際是對原有裝飾器的一個函數封裝,并傳回一個裝飾器。
類裝飾器:
相比如函數裝飾器,類裝飾器具有靈活度大、高内聚、封裝性等優點,使用類裝飾器還可以依靠内部的__call__方法,當使用@形式将裝飾器附加到函數上時,就會調用此方法。
比如下面的例子:
class use_decorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print "start execute decorator "
self.func()
print "end execute decorator "
@use_decorator
def test_func():
print "this is a test function"
if __name__ == '__main__':
test_func()
執行結果為:
start execute decorator
this is a test function
end execute decorator
相比于第一種函數裝飾器,類裝飾器是将原始方法傳遞到初始化__init__函數中,然後調用預設的__call__方法去完成裝飾器的執行邏輯。
最後我們看看多層裝飾器的嵌套使用,例子如下:
def decorator_A(func):
print "decorator A is call"
return func
def decorator_B(func):
print "decorator B is call"
return func
def decorator_C(func):
print "decorator C is call"
return func
@decorator_A
@decorator_B
@decorator_C
def test_func():
print "this is a test function"
if __name__ == '__main__':
test_func()
執行結果為:
decorator C is call
decorator B is call
decorator A is call
this is a test function
從結果分析,先是執行了最裡層裝飾器函數decorator_C,然後将傳入的方法test_func傳回給了外層裝飾器decorator_B,同樣執行完decorator_B後将被裝飾的方法作為參數傳遞給了裝飾器decorator_A,最終完成被裝飾器函數test_func的執行。整個過程相當于執行了:decorator_A(decorator_B(decorator_C(test_func)))。