1. 程式人生 > 程式設計 >Python 在函式上新增包裝器

Python 在函式上新增包裝器

問題

你想在函式上新增一個包裝器,增加額外的操作處理(比如日誌、計時等)。

解決方案

如果你想使用額外的程式碼包裝一個函式,可以定義一個裝飾器函式,例如:

import time
from functools import wraps

def timethis(func):
  '''
  Decorator that reports the execution time.
  '''
  @wraps(func)
  def wrapper(*args,**kwargs):
    start = time.time()
    result = func(*args,**kwargs)
    end = time.time()
    print(func.__name__,end-start)
    return result
  return wrapper

下面是使用裝飾器的例子:

>>> @timethis
... def countdown(n):
...   '''
...   Counts down
...   '''
...   while n > 0:
...     n -= 1
...
>>> countdown(100000)
countdown 0.008917808532714844
>>> countdown(10000000)
countdown 0.87188299392912
>>>

討論

一個裝飾器就是一個函式,它接受一個函式作為引數並返回一個新的函式。當你像下面這樣寫:

@timethis
def countdown(n):
  pass

跟像下面這樣寫其實效果是一樣的:

def countdown(n):
  pass
countdown = timethis(countdown)

順便說一下,內建的裝飾器比如 @staticmethod,@classmethod,@property 原理也是一樣的。例如,下面這兩個程式碼片段是等價的:

class A:
  @classmethod
  def method(cls):
    pass

class B:
  # Equivalent definition of a class method
  def method(cls):
    pass
  method = classmethod(method)

在上面的 wrapper() 函式中,裝飾器內部定義了一個使用 *args 和 **kwargs 來接受任意引數的函式。在這個函式裡面呼叫了原始函式並將其結果返回,不過你還可以新增其他額外的程式碼(比如計時)。然後這個新的函式包裝器被作為結果返回來代替原始函式。

需要強調的是裝飾器並不會修改原始函式的引數簽名以及返回值。使用 *args 和 **kwargs 目的就是確保任何引數都能適用。而返回結果值基本都是呼叫原始函式 func(*args,**kwargs) 的返回結果,其中func就是原始函式。

剛開始學習裝飾器的時候,會使用一些簡單的例子來說明,比如上面演示的這個。不過實際場景使用時,還是有一些細節問題要注意的。比如上面使用 @wraps(func) 註解是很重要的,它能保留原始函式的元資料(下一小節會講到),新手經常會忽略這個細節。接下來的幾個小節我們會更加深入的講解裝飾器函式的細節問題,如果你想構造你自己的裝飾器函式,需要認真看一下。

以上就是Python 在函式上新增包裝器的詳細內容,更多關於Python 新增包裝器的資料請關注我們其它相關文章!