當前位置:
首頁 > 知識 > Python 裝飾器基礎

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

:


            

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__)






  • 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

:


            

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
"

%

(

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


 


<

a

href

=

"http://www.jobbole.com/members/cxh1527"

>

@

Haha

</

a

>


class

Widget

(

object

)

:


    

""" class Widget """


 


if

__name__

==

"__main__"

:


    

w

=

Widget

()


    

print

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 面向對象(進階篇)
Python 面向對象(初級篇)
使用 Sphinx 製作簡潔而又美觀的文檔
直擊「黑產」痛點,金融反欺詐大賽等你奪魁

TAG:Python開發者 |

您可能感興趣

python裝飾器內容講解,告訴你python裝飾器是什麼
Python裝飾器的誕生過程
Python 小技巧 —— 用類寫裝飾器
Python退避及重試裝飾器:backoff
12步輕鬆搞定Python裝飾器
不懂裝飾器,就不是真正會 Python
Python核心編程的四大神獸:迭代器、生成器、閉包以及裝飾器
看完這篇文章還會不懂Python裝飾器?掐死小編吧
python裝飾器實現對異常代碼出現進行監控
Python高級編程,面向對象,迭代器生成器,閉包,裝飾器,詳細教程,必須收藏
PHP裝飾器模式之去年買了條狗
改變代碼又不破壞它,使用裝飾器吧
跨過這道裝飾器的坎,或許你就升級了