Python 裝飾器基礎
(點擊
上方藍字
,快速關注我們)
來源:xybaby
www.cnblogs.com/xybaby/p/6274187.html
如有好文章投稿,請點擊 → 這裡了解詳情
正文
一般來說,裝飾器是一個函數,接受一個函數(或者類)作為參數,返回值也是也是一個函數(或者類)。首先來看一個簡單的例子:
# -*- coding: utf-8 -*-
def
log_cost_time
(
func
)
:
def
wrapped
(
*
args
,
**
kwargs
)
:
import
time
begin
=
time
.
time
()
try
:
return
func
(
*
args
,
**
kwargs
)
finally
:
"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__"
:
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__)
functools.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
:
"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
"
%
(
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__"
:
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
<
a
href
=
"http://www.jobbole.com/members/cxh1527"
>
@
Haha
</
a
>
class
Widget
(
object
)
:
""" class Widget """
if
__name__
==
"__main__"
:
w
=
Widget
()
w
那什麼場景下有必要使用decorator呢,設計模式中有一個模式也叫裝飾器。我們先簡單回顧一下設計模式中的裝飾器模式,簡單的一句話概述
動態地為某個對象增加額外的責任
由於裝飾器模式僅從外部改變組件,因此組件無需對它的裝飾有任何了解;也就是說,這些裝飾對該組件是透明的。
下圖來自《設計模式Java手冊》或者GOF的《設計模式》
回到Python中來,用decorator語法實現裝飾器模式是很自然的,比如文中的示例代碼,在不改變被裝飾對象的同時增加了記錄函數執行時間的額外功能。當然,由於Python語言的靈活性,decorator是可以修改被裝飾的對象的(比如裝飾類的例子)。decorator在python中用途非常廣泛,下面列舉幾個方面:
(1)修改被裝飾對象的屬性或者行為
(2)處理被函數對象執行的上下文,比如設置環境變數,加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
看完本文有收穫?請轉
發分享給更多人
關注「P
ython開發者」,提升Python技能
※一步一步教你如何搭建自己的視頻聚合站
※Python 面向對象(進階篇)
※Python 面向對象(初級篇)
※使用 Sphinx 製作簡潔而又美觀的文檔
※直擊「黑產」痛點,金融反欺詐大賽等你奪魁
TAG:Python開發者 |
※python裝飾器內容講解,告訴你python裝飾器是什麼
※Python裝飾器的誕生過程
※Python 小技巧 —— 用類寫裝飾器
※Python退避及重試裝飾器:backoff
※12步輕鬆搞定Python裝飾器
※不懂裝飾器,就不是真正會 Python
※Python核心編程的四大神獸:迭代器、生成器、閉包以及裝飾器
※看完這篇文章還會不懂Python裝飾器?掐死小編吧
※python裝飾器實現對異常代碼出現進行監控
※Python高級編程,面向對象,迭代器生成器,閉包,裝飾器,詳細教程,必須收藏
※PHP裝飾器模式之去年買了條狗
※改變代碼又不破壞它,使用裝飾器吧
※跨過這道裝飾器的坎,或許你就升級了