首頁 >後端開發 >Python教學 >python裝飾器定義及運用實例講解

python裝飾器定義及運用實例講解

乌拉乌拉~
乌拉乌拉~原創
2018-08-22 16:17:371117瀏覽

在以下的文章之中我們來了解一下什麼是python裝飾器。了解一下關於python裝飾器的相關知識,以及python裝飾器的用法之類。好了,廢話不多說,我們開始進入接下來的文章吧。

python裝飾器

簡言之,python裝飾器就是用來拓展原來函數功能的一種函數,這個函數的特殊之處在於它的回傳值也是一個函數,使用python裝飾器的好處就是在不用更改原函數的程式碼前提下為函數增加新的功能。  

由於函數也是一個對象,而且函數物件可以被賦值給變量,所以,透過變數也能呼叫該函數。

>>> def now():
...     print('2015-3-25')
...
>>> f = now
>>> f()
2015-3-25

函數物件有一個__name__屬性,可以拿到函數的名字:

>>> now.__name__
'now'
>>> f.__name__
'now'

現在,假設我們要增強now()函數的功能,例如,在函數呼叫前後自動列印日誌,但又不希望修改now()函數的定義,這種在程式碼運行期間動態增加功能的方式,稱之為「裝飾器」(Decorator)。

本質上,decorator就是一個回傳函數的高階函數。所以,我們要定義一個能列印日誌的decorator,可以定義如下:

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)   
   return wrapper

觀察上面的log,因為它是一個decorator,所以接受一個函數當參數,並傳回一個函數。我們要藉助Python的@語法,把decorator置於函數的定義處:

@log
def now():
    print('2015-3-25')

呼叫now()函數,不僅會運行now()函數本身,還會在運行now()函數前打印一行日誌:

>>> now()
call now():
2015-3-25

把@log放到now()函數的定義處,相當於執行了語句:

now = log(now)

由於log()是一個decorator,回傳一個函數,所以,原來的now()函數仍然存在,只是現在同名的now變數指向了新的函數,於是呼叫now()將執行新函數,也就是在log()函數中傳回的wrapper()函數。

wrapper()函數的參數定義是(*args, **kw),因此,wrapper()函式可以接受任意參數的呼叫。在wrapper()函數內,先列印日誌,再緊接著呼叫原始函數。

如果decorator本身需要傳入參數,那就需要寫一個傳回decorator的高階函數,寫出來會更複雜。例如,要自訂log的文字:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))          
          return func(*args, **kw)  
        return wrapper  
     return decorator

這個3層嵌套的decorator用法如下:

@log('execute')
 def now():
   print('2015-3-25')

執行結果如下:

>>> now()
execute now():
2015-3-25

和兩層嵌套的decorator相比,3層巢狀的效果是這樣的:

>>> now = log('execute')(now)

我們來剖析上面的語句,先執行log('execute'),傳回的是decorator函數,再呼叫傳回的函數,參數是now函數,傳回值最終是wrapper函數。

以上兩種decorator的定義都沒有問題,但還差最後一步。因為我們講了函數也是對象,它有__name__等屬性,但你去看經過decorator裝飾之後的函數,它們的__name__已經從原來的'now'變成了'wrapper':

>>> now.__name__'
wrapper'

因為傳回的那個wrapper()函數名字就是'wrapper',所以,需要把原始函數的__name__等屬性複製到wrapper()函數中,否則,有些依賴函數簽章的程式碼執行就會出錯。

不需要寫wrapper.__name__ = func.__name__這樣的程式碼,Python內建的functools.wraps就是乾這個事的,所以,一個完整的decorator的寫法如下:

import functools
   def log(func):
   @functools.wraps(func)
     def wrapper(*args, **kw)
       print('call %s():' % func.__name__)      
     return func(*args, **kw)    
   return wrapper

或針對帶參數的decorator:

import functoolsdef log(text):
    def decorator(func):
      @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))        
          return func(*args, **kw)    
         return wrapper  
       return decorator

import functools是導入functools模組。模組的概念稍候講解。現在,只需記住在定義wrapper()的前面加上@functools.wraps(func)即可。

以上就是本篇文章所講述的所有內容,這篇文章主要介紹了相關於python裝飾器的知識,希望你能藉助資料從而理解上述所說的內容。希望我在這片文章所講述的內容能夠對你有幫助,讓你學習python更加輕鬆。

更多相關知識,請造訪php中文網Python教學欄位。

以上是python裝飾器定義及運用實例講解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn