裝飾器 Decorator
在代碼運行期間動態(tài)增加功能的方式,稱之為“裝飾器”。裝飾器本質(zhì)是高階函數(shù), 就是將函數(shù)作為參數(shù)進(jìn)行相應(yīng)運作,最后返回一個閉包代替原有函數(shù). 裝飾器本質(zhì)就是將原函數(shù)修飾為一個閉包(一個返回函數(shù)).
裝飾器在python中在函數(shù)/對象方法定義前使用@ 符號調(diào)用. 裝飾器可以在函數(shù)運行前進(jìn)行一些預(yù)處理, 例如檢查類型等.
@dec1
@dec2(arg1,arg2)
def test(arg):
pass
以上代碼等于dec1(dec2(arg1,arg2)(test(arg)))
簡單的裝飾器及其運行機(jī)制
例如:
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
@log
def now():
print '2015-10-26'
return "done"
now()
# 實際調(diào)用wrapper()
#>>> call now()
#>>> 2015-10-26
裝飾器運行機(jī)制
機(jī)制就是,調(diào)用now()實際調(diào)用log(now)() (前面@ 寫法后,實際運行now=log(now)),也就是運行了wrapper(),并把now函數(shù)原有參數(shù)傳遞給了wrapper函數(shù). wrapper在運行時,加入了新的處理print 'call %s():' % func.__name__ 一句, 并運行相應(yīng)傳遞參數(shù)的func(*args,**kw) 并把原有結(jié)果返回.
now=log(now) #->now=wrapper
result=now() #->wrapper()
>>>call now() #-> wrapper修飾部分
>>>2015-10-26 #-> 原函數(shù)部分執(zhí)行部分
print result
>>>done #-> 原函數(shù)的返回部分
所以裝飾器機(jī)制簡單地說就是要:
- 將原來函數(shù)通過裝飾器變成一個傳遞函數(shù)本身的高階函數(shù)(
@log 部分,now=log(now) )
- 新的高階函數(shù)要返回一個修飾函數(shù),從而使調(diào)用原函數(shù)時實際調(diào)用該部分. (
def wrapper()..return wrapper 部分)
- 新修飾函數(shù)進(jìn)行相應(yīng)修飾處理(print語句)后,執(zhí)行原函數(shù)并返回原函數(shù)值.
傳遞參數(shù)的裝飾器
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print '2015-10-26'
return "done"
now()
#>>> excute now()
#>>> 2015-10-26
裝飾器本身可以傳入?yún)?shù).不要小看這個傳入?yún)?shù), 傳入的參數(shù)因為占據(jù)了本身裝飾器的參數(shù), 所以需要新的一層裝飾器來處理原函數(shù). 上述機(jī)制:
now=log('execute')(now) #-> now=wrapper
#或者可以說f=log('execute') -> f=decorator
# f(now) -> decorator(now) -> wrapper
# now=wrapper
result=now() #wrapper()
實際now=log('execute')(now) 兩個參數(shù)表就是執(zhí)行了一次閉包decorator(now).執(zhí)行該閉包后返回的才是真正的裝飾器wrapper.
兩層閉包的機(jī)制可以保證傳遞參數(shù)給內(nèi)在的裝飾器wrapper.第一層將參數(shù)傳進(jìn)行生成第一層閉包對應(yīng)返回函數(shù),第二層則將該參數(shù)繼續(xù)留給真正的裝飾器閉包.
繼承原有函數(shù)信息
在以上裝飾器中, 其實質(zhì)都是now=wrapper , 此時我們要是輸出now.__name__ 得到的將是裝飾器wrapper的名字.可以用wrapper.__name__=func.__name__ 加在裝飾器內(nèi)部進(jìn)行原函數(shù)信息的繼承, 也可以使用functools.wraps 來實現(xiàn).
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
對于帶參數(shù)的裝飾器, 依然將@functools.wraps(func) 寫在實質(zhì)裝飾器wrapper前面.
其機(jī)制:
wrapper=functools.wraps(func)(wrapper)
#將原函數(shù)func信息給返回函數(shù)f=functools.wraps(func), f 閉包含有相應(yīng)func信息.
#用f 返回函數(shù)來修飾wrapper, 此時實際可以將wrapper的一些信息替換為原函數(shù)
對于很復(fù)雜的體系, 需要經(jīng)常定義一些高階函數(shù)對新函數(shù)進(jìn)行一系列處理, 此時定義裝飾器就可以省很多功夫. 但缺點是初學(xué)比較難以理解, 要對OOP十分熟悉.
對象方法變對象屬性的裝飾器@property和@*.setter
該裝飾器是python內(nèi)置的,是類中一個高級用法,作用是將一個方法名變成一個對象屬性. 類需要繼承于object相應(yīng)的類.
構(gòu)建相應(yīng)@property def prop(self):return self._prop 就可以直接obj.prop 來將方法變成獲取對象屬性的調(diào)用形式.而相應(yīng)@prop.setter def prop(self,value): self._prop=value 就可以實現(xiàn)obj.prop=value 將方法轉(zhuǎn)為對象屬性的賦值.而且好處還可以在此加入屬性的值的檢查. obj._prop只是相應(yīng)儲存的儲存地方,名字也是無限制的.
不定義setter而只定義property的話,該屬性就是只讀的不能修改的!!
例如:
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
@property
def fail(self):
return True if (self.score <60) else False
# (self.score <60) and return True or return False
s = Student()
s.score = 60 # OK,實際轉(zhuǎn)化為s.set_score(60)
s.score # OK,實際轉(zhuǎn)化為s.get_score()
### 60
s.score = 9999
### Traceback (most recent call last):
### ...
### ValueError: score must between 0 ~ 100!
s.fail
### False
s.fail=True
### Traceback (most recent call last)
### ...
### AttributeError: can't set attribute
本博文已合并到Python語法匯總中, 不再更新.
|