@decorator
什麼是裝飾器:python
問題:
定義一個函數後
在運行時動態增長功能
又不想改動函數自己的代碼
示例:數據庫
# 但願對下列函數調用增長log功能,打印出函數調用: def f1(x): return x*2 def f2(x): return x*x def f3(x): return x*x*x
方法1:直接修改原函數的定義app
def f1(x): print('call f1') return x*2 def f2(x): print('call f2') return x*x def f3(x): print('call f3') return x*x*x
有沒有更簡單的方法?函數
高階函數 1. 能夠接受函數做爲參數 2. 能夠返回函數 是否能夠接受一個函數,對其進行包裝,而後返回一個新函數?
方法2:經過高階函數返回新函數(裝飾器函數):post
def f1(x): return x*2 def new_fn(f): def fn(x): print('call'+f.__name__+'()') return f(x) return fn
調用1:性能
g1 = new_fn(f1) print( g1(5) )
調用2:spa
f1 = new_fn(f1) print( f1(5) )
python內置@語法就是爲了簡化裝飾器調用翻譯
@new_fn def f1(x): return x*2
等同於:日誌
def f1(x): return x*2 f1 = new_fn(f1)
能夠極大簡化代碼,避免每一個函數編寫重複性的代碼 打印日誌:@log 檢測性能:@performance 數據庫事務:@transaction URL路由:@post('/register')
1. 若是自定義函數存在兩個形參,上述裝飾器函數內部固定寫了一個形參,會出現錯誤怎麼辦:code
@new_fn def add(x,y): return x+y # 這裏會出現錯誤,因此須要更改 new_fn 函數以下: def new_fn(f): def fn(*args, **kwargs): print('call'+f.__name__+'()') return f(*args, **kwargs) return fn
2. 請編寫一個@performance,它能夠打印出函數調用的時間。計算函數調用的時間能夠記錄調用先後的當前時間戳,而後計算兩個時間戳的差。
import time def performance(f): def fn(*args, **kw): t1 = time.time() r = f(*args, **kw) t2 = time.time() print 'call %s() in %fs' % (f.__name__, (t2 - t1)) return r return fn @performance def factorial(n): return reduce(lambda x,y: x*y, range(1, n+1)) print factorial(10)
3.上述裝飾器函數只能接受一個函數參數,若是裝飾器函數須要傳入額外的參數怎麼辦?
@log('DEBUG') def my_func(): pass
把上面的定義翻譯成高階函數的調用,就是:
my_func = log('DEBUG')(my_func)
上面的語句看上去仍是比較繞,再展開一下:
log_decorator = log('DEBUG') my_func = log_decorator(my_func)
上面的語句又至關於:
log_decorator = log('DEBUG') @log_decorator def my_func(): pass
因此,帶參數的log函數首先返回一個decorator函數,再讓這個decorator函數接收my_func並返回新函數:
def log(prefix): def log_decorator(f): def wrapper(*args, **kw): print '[%s] %s()...' % (prefix, f.__name__) return f(*args, **kw) return wrapper return log_decorator @log('DEBUG') def test(): pass print test()
上述第二個例子:@performance只能打印秒,請給 @performace 增長一個參數,容許傳入's'或'ms':
import time import functools def performance(unit): def perf_decorator(f): @functools.wraps(f) def wapper(*args, **kw): t1 = time.time() r = f(*args, **kw) t2 = time.time() t = (t2 - t1) * 1000 if unit=='ms' else (t2 - t1) print 'call %s() in %f %s' % (f.__name__, t,unit) return r return wapper return perf_decorator @performance('ms') def factorial(n): return reduce(lambda x,y: x*y, range(1, n+1)) print factorial(10) print factorial.__name__