Python裝飾器
- 裝飾器的本質(zhì)是什么?
裝飾器等價于高階函數(shù)疏咐,形如myfunc=decorator(myfunc)纤掸,接受函數(shù)或類作為輸入,以函數(shù)或類作為輸出浑塞。在@語法糖中借跪,并沒有要求輸出為一個函數(shù)或者類。即
def decorator(func):
return None
@decorator
def foo():
pass
也可以作為@decorator語法裝飾函數(shù)或類酌壕。
所以掏愁,可以將裝飾器當(dāng)成是一個接受函數(shù)或類為輸入的函數(shù)即可。
- 裝飾器的使用場景
- 插入日志
- 性能測試
- 事務(wù)處理
- 權(quán)限認(rèn)證
- 修改傳入?yún)?shù)/參數(shù)校驗
- 結(jié)果預(yù)處理
- 截斷函數(shù)的執(zhí)行
- …
- 編寫不帶裝飾器參數(shù)的裝飾器
def decorator(func):
# do something when decorator called
def _deco(*args, **kw):
# do something before func called
ret = func(*args, **kw)
# do something after func called
return ret
return _deco
@decorator
def myfunc(*args, **kw):
print "myfunc called"
return True
內(nèi)部函數(shù)“_deco”和myfunc的參數(shù)結(jié)構(gòu)需要一致卵牍,當(dāng)然不是要求形式上的一致果港。如參數(shù)為類的裝飾器,如下
def singleton(cls):
# do something when decorator called
instances = {}
def _getinstance(*args, **kw):
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return _getinstance
@singleton
class Myclass(object):
def __init__(self, *args, **kw):
pass
以上示例為構(gòu)建單例模式的裝飾器版本糊昙。
- 編寫帶裝飾器參數(shù)的裝飾器
帶參數(shù)的裝飾器辛掠,等價于形如myfunc=decorator(a, b)(myfunc)的函數(shù)調(diào)用。decorator(a, b)返回一個不帶裝飾器參數(shù)的裝飾器释牺,即過程為:decorator1 = decorator(a, b) 萝衩;myfunc = decorator1(myfunc)。形式上多了一層函數(shù)没咙,不過也可以使用柯里化的方式構(gòu)建decorator1猩谊。如下例:
from functools import partial
def decorator(a, func):
# do something when decorator called
def _deco(*args, **kw):
# do something before func called
ret = func(*args, **kw)
# do something after func called
return ret
return _deco
# or
def decorator_out(*args, **kwargs):
# do something when decorator_out called with parameters
def _outer(func):
# do something when decorator_out called
def _deco(*arg, **kw):
# do something before func called
# of course `*args` or `**kwargs`
ret = func(*arg, **kw)
# do something after func called
return _deco
return _outer
@partial(decorator, "param1")
def myfunc():
print "myfunc"
@decorator_out("params1")
def myfunc1():
print "myfunc1"
- functools.wraps
因為Python中__name__
或__doc__
等metadata是在函數(shù)定義中生成的,而無法在執(zhí)行過程中生成祭刚,所以myfunc=decorator(myfunc)執(zhí)行之后牌捷,myfunc的metadata會發(fā)生變化队塘,如myfunc.__name__
,解決方案是裝飾器中宜鸯,加上functools.wraps憔古。如上例:
from functools import wraps
def decorator_out(*args, **kwargs):
# do something when decorator_out called with parameters
def _outer(func):
# do something when decorator_out called
@wraps(func)
def _deco(*arg, **kw):
# do something before func called
# of course `*args` or `**kwargs`
ret = func(*arg, **kw)
# do something after func called
return ret
return _deco
return _outer