裝飾器(Decorators)
裝飾器是這樣一種設(shè)計模式:如果一個類希望添加其他類的一些功能,而不希望通過繼承或是直接修改源代碼實現(xiàn),那么可以使用裝飾器模式。簡單來說Python中的裝飾器就是指某些函數(shù)或其他可調(diào)用對象,以函數(shù)或類作為可選輸入?yún)?shù),然后返回函數(shù)或類的形式。通過這個在Python2.6版本中被新加入的特性可以用來實現(xiàn)裝飾器設(shè)計模式。
順便提一句,在繼續(xù)閱讀之前,如果你對Python中的閉包(Closure)概念不清楚,請查看本文結(jié)尾后的附錄,如果沒有閉包的相關(guān)概念,很難恰當(dāng)?shù)睦斫釶ython中的裝飾器。
在Python中,裝飾器被用于用@語法糖修辭的函數(shù)或類。現(xiàn)在讓我們用一個簡單的裝飾器例子來演示如何做一個函數(shù)調(diào)用日志記錄器。在這個例子中,裝飾器將時間格式作為輸入?yún)?shù),在調(diào)用被這個裝飾器裝飾的函數(shù)時打印出函數(shù)調(diào)用的時間。這個裝飾器當(dāng)你需要手動比較兩個不同算法或?qū)崿F(xiàn)的效率時很有用。
def logged(time_format): def decorator(func): def decorated_func(*args, **kwargs): print "- Running '%s' on %s " % ( func.__name__, time.strftime(time_format) ) start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print "- Finished '%s', execution time = %0.3fs " % ( func.__name__, end_time - start_time ) return result decorated_func.__name__ = func.__name__ return decorated_func return decorator
來看一個例子,在這里add1和add2函數(shù)被logged修飾,下面給出了一個輸出示例。請注意在這里時間格式參數(shù)是存儲在被返回的裝飾器函數(shù)中(decorated_func)。這就是為什么理解閉包對于理解裝飾器來說很重要的原因。同樣也請注意返回函數(shù)的名字是如何被替換為原函數(shù)名的,以防萬一如果它還要被使用到,這是為了防止混淆。Python默認(rèn)可不會這么做。
@logged("%b %d %Y - %H:%M:%S")def add1(x, y): time.sleep(1) return x + y @logged("%b %d %Y - %H:%M:%S")def add2(x, y): time.sleep(2) return x + y print add1(1, 2)print add2(1, 2) # Output:- Running 'add1' on Jul 24 2013 - 13:40:47- Finished 'add1', execution time = 1.001s3- Running 'add2' on Jul 24 2013 - 13:40:48- Finished 'add2', execution time = 2.001s3
如果你足夠細(xì)心,你可能會注意到我們對于返回函數(shù)的名字__name__有著特別的處理,但對其他的注入__doc__或是__module__則沒有如此。所以如果,在這個例子中add函數(shù)有一個doc字符串的話,它就會被丟棄。那么該如何處理呢?我們當(dāng)然可以像處理__name__那樣對待所有的字段,不過如果在每個裝飾器內(nèi)都這么做的話未免太繁冗了。這就是為何functools模塊提供了一個名為wraps的裝飾器的原因,那正是為了處理這種情況??赡茉诶斫庋b飾器的過程中會被迷惑,不過當(dāng)你把裝飾器看成是一個接收函數(shù)名作為輸入?yún)?shù)并且返回一個函數(shù),這樣就很好理解了。我們將在下個例子中使用wraps裝飾器而不是手動去處理__name__或其他屬性。
新聞熱點
疑難解答
圖片精選