新聞中心
正文

創(chuàng)新互聯公司總部坐落于成都市區(qū),致力網站建設服務有做網站、成都網站設計、網絡營銷策劃、網頁設計、網站維護、公眾號搭建、微信小程序定制開發(fā)、軟件開發(fā)等為企業(yè)提供一整套的信息化建設解決方案。創(chuàng)造真正意義上的網站建設,為互聯網品牌在互動行銷領域創(chuàng)造價值而不懈努力!
一般來說,裝飾器是一個函數,接受一個函數(或者類)作為參數,返回值也是也是一個函數(或者類)。首先來看一個簡單的例子:
- # -*- coding: utf-8 -*-
- def log_cost_time(func):
- def wrapped(*args, **kwargs):
- import time
- begin = time.time()
- try:
- return func(*args, **kwargs)
- finally:
- print 'func %s cost %s' % (func.__name__, time.time() - begin)
- return wrapped
- @log_cost_time
- def complex_func(num):
- ret = 0
- for i in xrange(num):
- ret += i * i
- return ret
- #complex_func = log_cost_time(complex_func)
- if __name__ == '__main__':
- print complex_func(100000)
- code snippet 0
代碼中,函數log_cost_time就是一個裝飾器,其作用也很簡單,打印被裝飾函數運行時間。
裝飾器的語法如下:
- @dec
- def func():pass
本質上等同于: func = dec(func)。
在上面的代碼(code snippet 0)中,把line12注釋掉,然后把line18的注釋去掉,是一樣的效果。另外staticmethod和classmethod是兩個我們經常在代碼中用到的裝飾器,如果對pyc反編譯,得到的代碼一般也都是 func = staticmthod(func)這種模式。當然,@符號的形式更受歡迎些,至少可以少拼寫一次函數名。
裝飾器是可以嵌套的,如
- @dec0
- @dec1
- def func():pass
等將于 func = dec0(dec1(fun))。
裝飾器也有“副作用“”,對于被log_cost_time裝飾的complex_calc, 我們查看一下complex_func.__name__,輸出是:”wrapped“”。額,這個是log_cost_time里面inner function(wrapped)的名字,調用者當然希望輸出是”complex_func”,為了解決這個問題,python提供了兩個函數。
- functools.update_wrapper
原型: functools.update_wrapper(wrapper, wrapped[, assigned][, updated])
第三個參數,將wrapped的值直接復制給wrapper,默認為(__doc__, __name__, __module__)
第四個參數,update,默認為(__dict__)
- unctools.wraps: update_wrapper的封裝
This is a convenience function for invoking partial(update_wrapper,wrapped=wrapped,assigned=assigned,updated=updated) as a function decorator when defining a wrapper function.
簡單改改代碼:
- import functools
- def log_cost_time(func):
- @functools.wraps(func)
- def wrapped(*args, **kwargs):
- import time
- begin = time.time()
- try:
- return func(*args, **kwargs)
- finally:
- print 'func %s cost %s' % (func.__name__, time.time() - begin)
- return wrapped
再查看complex_func.__name__ 輸出就是 “complex_func”
裝飾器也是可以帶參數的。我們將上面的代碼略微修改一下:
- def log_cost_time(stream):
- def inner_dec(func):
- def wrapped(*args, **kwargs):
- import time
- begin = time.time()
- try:
- return func(*args, **kwargs)
- finally:
- stream.write('func %s cost %s \n' % (func.__name__, time.time() - begin))
- return wrapped
- return inner_dec
- import sys
- @log_cost_time(sys.stdout)
- def complex_func(num):
- ret = 0
- for i in xrange(num):
- ret += i * i
- return ret
- if __name__ == '__main__':
- print complex_func(100000)
- code snippet 1
log_cost_time函數也接受一個參數,該參數用來指定信息的輸出流,對于帶參數的decorator
- @dec(dec_args)
- def func(*args, **kwargs):pass
等價于 func = dec(dec_args)(*args, **kwargs)。
裝飾器對類的修飾也是很簡單的,只不過平時用得不是很多。舉個例子,我們需要給修改類的__str__方法,代碼很簡單。
- def Haha(clz):
- clz.__str__ = lambda s: "Haha"
- return clz
- @Haha
- class Widget(object):
- ''' class Widget '''
- if __name__ == '__main__':
- w = Widget()
- print w
那什么場景下有必要使用decorator呢,設計模式中有一個模式也叫裝飾器。我們先簡單回顧一下設計模式中的裝飾器模式,簡單的一句話概述
動態(tài)地為某個對象增加額外的責任
由于裝飾器模式僅從外部改變組件,因此組件無需對它的裝飾有任何了解;也就是說,這些裝飾對該組件是透明的。
下圖來自《設計模式Java手冊》或者GOF的《設計模式》
回到Python中來,用decorator語法實現裝飾器模式是很自然的,比如文中的示例代碼,在不改變被裝飾對象的同時增加了記錄函數執(zhí)行時間的額外功能。當然,由于Python語言的靈活性,decorator是可以修改被裝飾的對象的(比如裝飾類的例子)。decorator在python中用途非常廣泛,下面列舉幾個方面:
(1)修改被裝飾對象的屬性或者行為
(2)處理被函數對象執(zhí)行的上下文,比如設置環(huán)境變量,加log之類
(3)處理重復的邏輯,比如有N個函數都可能跑出異常,但是我們不關心這些異常,只要不向調用者傳遞異常就行了,這個時候可以寫一個catchall的decorator,作用于所用可能跑出異常的函數
- def catchall(func):
- @functools.wraps(func)
- def wrapped(*args, **kwargs):
- try:
- return func(*args, **kwargs)
- except:
- pass
- return wrapped
(4)框架代碼,如flask, bottle等等,讓使用者很方便就能使用框架,本質上也避免了重復代碼。
decorator的奇妙應用往往超出相應,經常在各種源碼中看到各種神奇的用法,酷殼這篇文章舉的例子也不錯。
參考
- pep 0318:https://www.python.org/dev/peps/pep-0318/#syntax-alternatives
- PYTHON修飾器的函數式編程:http://coolshell.cn/articles/11265.html
本文名稱:PythonDecorator基礎
本文網址:http://m.5511xx.com/article/djhddhg.html


咨詢
建站咨詢
