問題:python
經過高階段函數返回一個新函數數據庫
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 f1 = new_fn(f1) #f1的原始定義函數完全被隱藏了 print (f1(5)) #輸出: #call f1() #10
python內置的@語法就是爲了簡化裝飾器app
使用 decorator 用Python提供的 @ 語法,這樣能夠避免手動編寫 f = decorate(f) 這樣的代碼。函數
相似上述的方法2post
裝飾器的做用性能
能夠極大的簡化代碼,避免每一個函數編寫重複性代碼spa
舉例:日誌
def log(f): def fn(x): print 'call ' + f.__name__ + '()...' return f(x) return fn @log def factorial(n): return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10) #結果 call factorial()... 3628800
對於不是一個參數的函數,須要修改:code
要讓 @log 自適應任何參數定義的函數,能夠利用Python的 *args 和 **kw,保證任意個數的參數老是能正常調用:orm
def log(f): def fn(*args, **kw): print 'call ' + f.__name__ + '()...' return f(*args, **kw) return fn
@log def add(x, y): return x + y print add(1, 2)
帶參數的log函數首先返回一個decorator函數,再讓這個decorator函數接收my_func並返回新函數:
def log(prefix): def log_decorator(f):#標準decorator def wrapper(*args, **kw): print '[%s] %s()...' % (prefix, f.__name__) return f(*args, **kw) return wrapper return log_decorator #返回log @log('DEBUG') def test(): pass print test()
#執行結果 [DEBUG] test()... None
@decorator能夠動態實現函數功能的增長,可是,通過@decorator「改造」後的函數,和原函數相比,有所不一樣:
def f1(x): pass print f1.__name__ #輸出 f1
def log(f): def wrapper(*args, **kw): print 'call...' return f(*args, **kw) return wrapper @log def f2(x): pass print f2.__name__ #輸出 wrapper
因爲decorator返回的新函數函數名已經不是'f2',而是@log內部定義的'wrapper'。這對於那些依賴函數名的代碼就會失效。decorator還改變了函數的__doc__等其它屬性。若是要讓調用者看不出一個函數通過了@decorator的「改造」,就須要把原函數的一些屬性複製到新函數中:
方法1:
def log(f): def wrapper(*args, **kw): print 'call...' return f(*args, **kw) wrapper.__name__ = f.__name__ wrapper.__doc__ = f.__doc__ return wrapper
方法2:Python內置的functools能夠用來自動化完成這個「複製」的任務(推薦使用)
import functools def log(f): @functools.wraps(f) # def wrapper(*args, **kw): print 'call...' return f(*args, **kw) return wrapper
最後須要指出,因爲咱們把原函數簽名改爲了(*args, **kw),所以,沒法得到原函數的原始參數信息。
當一個函數有不少參數時,調用者就須要提供多個參數。若是減小參數個數,就能夠簡化調用者的負擔。
functools.partial就是幫助咱們建立一個偏函數的,不須要咱們本身定義int2(),能夠直接使用下面的代碼建立一個新的函數int2:
>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85
舉例:在sorted這個高階函數中傳入自定義排序函數就能夠實現忽略大小寫排序。
import functools
sorted_ignore_case = functools.partial(sorted,key=str.lower) print sorted_ignore_case(['bob', 'about', 'Zoo', 'Credit'])