當前位置:
首頁 > 知識 > Python Decorator 基礎

Python Decorator 基礎

(點擊

上方藍字

,快速關注我們)




來源: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__)






  • 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
"

 % 

(

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呢,設計模式中有一個模式也叫裝飾器。我們先簡單回顧一下設計模式中的裝飾器模式,簡單的一句話概述





動態地為某個對象增加額外的責任





由於裝飾器模式僅從外部改變組件,因此組件無需對它的裝飾有任何了解;也就是說,這些裝飾對該組件是透明的。




下圖來自《設計模式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 網路庫 gevent 介紹
從「猿」到「金剛」,機器學習讓你在職業生涯超進化!
用 Python 進行貝葉斯模型建模(3)
IEEE Spectrum 2017 編程語言排行:Python 奪冠

TAG:Python開發者 |

您可能感興趣

Python之tworoutine
Python中使用Type hinting 和 annotations
在Python中使用Elasticsearch
Python模塊——contextlib和urllib
Python 標準庫精華: collections.Counter
如何用 Python 寫 Alfred Workflow
為什麼Python如此火?Why Python is so popular?
Bayesian Personalized Ranking 演算法解析及Python實現
Python鏈式操作:PyFunctional
Python 模塊 urllib.parse
Python 的 ChatOps 庫:Opsdroid 和 Errbot
基於Asyncio的Python微框架:Quart
Process-Forest-Window進程日誌分析工具;python版的BloodHound
可以拋棄 Python?Google 開源 Swift for TensorFlow 意味什麼
基於 Python Schema 的機器學習庫——Smart Fruit
如何 Docker 化 Python Django 應用程序
Python爬蟲學習Scrapy之Spiders
可以拋棄 Python 了?Google 開源 Swift for TensorFlow 意味著什麼
Python async/await 介紹
Docker 安裝 Python