標籤:lambda python performance
@decorator可以動態實現函數功能的增加,但是,經過@decorator“改造”後的函數,和原函數相比,除了功能多一點外,有沒有其它不同的地方?
在沒有decorator的情況下,列印函數名:
def f1(x): passprint f1.__name__
輸出: f1
有decorator的情況下,再列印函數名:
def log(f): def wrapper(*args, **kw): print ‘call...‘ return f(*args, **kw) return wrapper@logdef f2(x): passprint f2.__name__
輸出: wrapper
可見,由於decorator返回的新函數函數名已經不是‘f2‘,而是@log內部定義的‘wrapper‘。這對於那些依賴函數名的代碼就會失效。decorator還改變了函數的__doc__等其它屬性。如果要讓調用者看不出一個函數經過了@decorator的“改造”,就需要把原函數的一些屬性複製到新函數中:
def log(f): def wrapper(*args, **kw): print ‘call...‘ return f(*args, **kw) wrapper.__name__ = f.__name__ wrapper.__doc__ = f.__doc__ return wrapper
這樣寫decorator很不方便,因為我們也很難把原函數的所有必要屬性都一個一個複製到新函數上,所以Python內建的functools可以用來自動化完成這個“複製”的任務:
import functoolsdef log(f): @functools.wraps(f) def wrapper(*args, **kw): print ‘call...‘ return f(*args, **kw) return wrapper
最後需要指出,由於我們把原函數簽名改成了(*args, **kw),因此,無法獲得原函數的原始參數資訊。即便我們採用固定參數來裝飾只有一個參數的函數:
def log(f): @functools.wraps(f) def wrapper(x): print ‘call...‘ return f(x) return wrapper
也可能改變原函數的參數名,因為新函數的參數名始終是 ‘x‘,原函數定義的參數名不一定叫 ‘x‘。
任務
請思考帶參數的@decorator,@functools.wraps應該放置在哪:
def performance(unit): def perf_decorator(f): def wrapper(*args, **kw): ??? return wrapper return perf_decorator
-
?不會了怎麼辦
-
注意@functools.wraps應該作用在返回的新函數上。
參考代碼:
import time, functoolsdef performance(unit): def perf_decorator(f): @functools.wraps(f) def wrapper(*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 wrapper return perf_decorator@performance(‘ms‘)def factorial(n): return reduce(lambda x,y: x*y, range(1, n+1))print factorial.__name__
Python完善decorator