假設我們要增強now()
函式的功能,比如,在函式呼叫前後自動列印日誌,但又不希望修改now()
函式的定義,這種在**執行期間動態增加功能的方式,稱之為「裝飾器」(decorator)。---即為已經存在的物件新增額外的功能(為了減少重複寫**)。
裝飾器的需求場景:插入日誌、效能測試、事務處理、快取、許可權校驗等。
一、函式裝飾器
1、裝飾器的擴充套件功能**寫在什麼地方
本質上,decorator就是乙個返回函式的高階函式。所以,我們要定義乙個能列印日誌的decorator,可以定義如下:
def
log(func):
def(*args, **kw):
print('call %s():' % func.__name__)
#裝飾器的功能擴充套件**區
return
func(*args, **kw)
return
2、@符號的作用
@符號是裝飾器的語法糖,在定義函式的時候使用,避免再一次賦值操作。相當於執行了語句:
now = log(now)
觀察上面的log
,因為它是乙個decorator,所以接受乙個函式作為引數,並返回乙個函式。我們要借助python的@語法,把decorator置於函式的定義處:
@log
#相當於在定義函式now()之後,執行一條命令:now=log(now)
defnow
():
print(
'2015-3-25'
)
呼叫now()
函式,不僅會執行now()
函式本身,還會在執行now()
函式前列印一行日誌:
>>> now()
call now():
2015-3-25
把@log
放到now()
函式的定義處
,
now = log(now)
3、裝飾器中的函式與原函式之間的關係
在函式內,首先列印日誌,再緊接著呼叫原始函式。
由於log()
是乙個decorator,返回乙個函式,所以,
原來的now()
函式仍然存在,只是現在同名的now
變數指向了新的函式,於是呼叫now()
將執行新函式,
。二、帶引數的裝飾器
裝飾器的語法允許我們在呼叫時,提供其它引數,比如@decorator(x),這樣,就為裝飾器的編寫和使用提供了更大的靈活性。它實際上是對原有裝飾器的乙個函式封裝,並返回乙個裝飾器。我們可以將它理解為乙個含有引數的閉包。
三、類裝飾器
類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器還可以依靠類內部的__call__方法,當使用@形式將裝飾器附加到函式上時,就會呼叫此方法。
四、內建裝飾器
@a@b
@cdef f()
等效於f=a(b(c(f)))
五、缺點
使用裝飾器極大地復用了**,但是他有乙個缺點就是原函式的元資訊不見了,比如函式的
docstring
、__name__
、引數列表
。不需要編寫
這樣的**,python內建的functools.wraps
就是幹這個事的,所以,乙個完整的decorator的寫法如下:
import
functools
deflog
(func):
@functools.wraps(func)
#functools本身也是個裝飾器,它保證原有函式的相關屬性不被wraps這個函式取代而發生不會發生變化的問題出現
def(*args, **kw):
print(
'call %s():'
% func.__name__)
return
func(*args, **kw)
return
或者針對帶引數的decorator:
import functools
deflog
(text):
defdecorator
(func):
@functools.wraps(func)
def(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return decorator
即可。
函式式程式設計(4) 裝飾器
裝飾器 由於函式也是乙個物件,而且函式物件可以被賦值給變數,所以,通過變數也能呼叫該函式。def now print 2013 12 25 f now f 2013 12 25 函式物件有乙個 name 屬性,可以拿到函式的名字 now.name now f.name now 現在,假設我們要增強n...
python 函式式程式設計 高階函式 裝飾器
coding gb2312 coding utf 8 高階函式 import math def is sqr x y int math.sqrt x return x y y print filter is sqr,range 1,101 返回函式 作用 延遲執行 def calc prod lst...
Python學習筆記 函式式程式設計 裝飾器
根據廖雪峰python教程整理 由於函式也是乙個物件,而且函式物件可以被賦值給變數,所以,通過變數也能呼叫該函式。def now print 2013 12 25 f now f 2013 12 25 函式物件有乙個 name 屬性,可以拿到函式的名字 now.name now f.name now...