一、裝飾器python
不修改被裝飾函數的定義,可是能夠在代碼運行期間動態增長功能的方式,稱之爲「裝飾器「,
本質上,裝飾器decorator就是一個返回函數的高階函數設計模式
#!/usr/bin/env python #-*- coding:utf-8 -*- import math# 導入數學公式模塊 from collections import Iterable# 判斷是否爲可迭代對象 import os# 導入os模塊 import functools# 導入functools.wraps # Python將一切視爲object的子類,即一切都是對象,固然函數也是一個對象,能夠像變量同樣被傳遞和指向 def foo(): print "I`m foo" f = foo f() # 函數對象有一個__name__的屬性,能夠獲得函數的名字 print foo.__name__ print f.__name__ # ★不修改被裝飾函數的定義,可是能夠在代碼運行期間動態增長功能的方式,稱之爲「裝飾器「, # 例子:在函數調用先後自動打印日誌 def logging1(func):# 裝飾器函數logging1 def wrapper(*args, **kw):# 可變參數和關鍵字參數 print '%s is running...' % func.__name__ return func(*args, **kw) return wrapper # logging1是一個裝飾器,它能接收一個函數作參數,並返回一個函數 @logging1 def foo(): print "I`m foo" # @符號是裝飾器的語法糖,在定義函數的時候使用,避免再一次賦值操做 foo()# 調用foo()函數 # @logging1放到foo()函數的定義處,調用被裝飾函數foo,不只會執行被裝飾函數foo,還會執行logging1裝飾器 # @logging1至關於執行了foo = logging1(foo)語句 # 分析:因爲logging1() 是一個decorator,返回一個函數,原來的foo()函數仍然存在,只是如今同名的foo變量指向了新的函數,即在logging1()函數中返回的wrapper()函數;因而調用foo()將執行新wrapper()函數 # wrapper()函數的參數定義是(*args, **kw),所以,wrapper() 函數能夠接受任意參數的調用。在wrapper()函數內,首先打印日誌,再緊接着調用原始函數foo() # ★有參數的裝飾器,須要編寫一個返回decorator的高階函數 # 裝飾器 def logging2(info): def decorator(func): def wrapper(*args, **kw): print '%s,%s is running...' % (info, func.__name__) return func(*args, **kw) return wrapper return decorator # 用法 @logging2('Waiting') def foo(): print "I`m foo" # 調用foo()函數 foo() # 這種三層嵌套結構的裝飾器,執行過程是foo = logging2('Waiting')(foo) # 分析:首先執行logging2('Waiting'),返回的是decorator函數,再調用返回的decorator函數,參數是now函數,最終返回值是wrapper函數
運行效果:
二、內置的functools.wraps裝飾器app
設計模式之裝飾模式decorator函數
使用Python內置的functools.wraps,在實現以前加上functools的wraps,它能保留原有函數的名稱和docstring
Python除了能支持OOP的decorator外,直接從語法層次支持decorator。Python的decorator能夠用函數實現,也能夠用類實現。設計
# ★函數也是對象,它有__name__ 等屬性,但上面兩個例子通過decorator裝飾以後的函數,它們的__name__ 已經從原來的'now' 變成了'wrapper' print u'裝飾後函數的名字:',foo.__name__ # 由於返回的那個函數是wrapper() ,因此,結果就是'wapper',如今須要把原始函數的__name__等屬性複製到wrapper() 函數中,不然,有些依賴函數簽名的代碼執行就會出錯。 # 可使用wrapper.__name__ = func.__name__ def logging3(info): def decorator(func): def wrapper(*args, **kw): wrapper.__name__ = func.__name__ print '%s,%s is running...' % (info, func.__name__) return func(*args, **kw) return wrapper return decorator @logging3('Waiting') def foo(): print "I`m foo" foo() print u'裝飾後函數的名字:',foo.__name__ # 無參裝飾器使用functools.wraps def logging4(func):# 裝飾器函數logging4 @functools.wraps(func)# 使用functools.wraps保留原來函數的信息 def wrapper(*args, **kw):# 可變參數和關鍵字參數 print '%s is running...' % func.__name__ return func(*args, **kw) return wrapper @logging4 def foo(): print "I`m foo" foo() print u'無參裝飾器使用functools.wraps:',foo.__name__ # 有參裝飾器使用functools.wraps def logging5(info): def decorator(func): @functools.wraps(func)# 使用functools.wraps保留原來函數的信息 def wrapper(*args, **kw): print '%s,%s is running...' % (info, func.__name__) return func(*args, **kw) return wrapper return decorator @logging5('Waiting') def foo(): print "I`m foo" foo() print u'有參裝飾器使用functools.wraps:',foo.__name__ # ★編寫一個decorator,能在函數調用的先後打印出'begin call' 和'end call' 的日誌 def logging6(info): def decorator(func): @functools.wraps(func)# 使用functools.wraps保留原來函數的信息 def wrapper(*args, **kw): print '%s...,begin call %s()' % (info, func.__name__)# 調用前 result = func(*args, **kw)# 由於不能直接返回,因此須要暫存結果 print 'end call %s()' % (func.__name__)# 調用後 return result return wrapper return decorator # 組建裝飾器 @logging6('Waiting') def foo(): print "I`m foo" foo() # ★寫出一個@log的decorator。使它既支持:@log,又支持:@log('execute') def logging7(info): if isinstance(info,str): # 有參裝飾器 def decorator(func): @functools.wraps(func) def wrapper(*args,**kw): print u'%s,有參%s is running...' %(info,func.__name__) func(*args,**kw) print u'有參%s is enddning...' %(func.__name__) return wrapper return decorator else: # 無參裝飾器 @functools.wraps(info) # 此時info就是被裝飾函數 def wrapper(*args,**kw): print u'請等待,無參%s is running...' %(info.__name__) info(*args,**kw) print u'無參%s is enddning...' %(info.__name__) return wrapper # 組建無參裝飾器 @logging7 def foo(): print "I`m foo" foo() # 組建有參裝飾器 @logging7('Waiting') def foo(): print "I`m foo" foo()
運行效果:日誌
三、functools.partial偏函數code
偏函數:經過設定參數的默認值,下降函數調用的難度對象
functools模塊除了wraps裝飾器外,還有好多其餘函數,例如偏函數functools.partial,區別於數學意義上的偏函數
utf-8
#!/usr/bin/env python #-*- coding:utf-8 -*- import math# 導入數學公式模塊 from collections import Iterable# 判斷是否爲可迭代對象 import os# 導入os模塊 import functools# 導入functools.wraps # int():默認參數base=10 print u'默認十進制', int('12') # 必選參數 # 結果:123456 print u'八進制數:', int ('12',base = 8)# 默認參數 # 結果:10 # base表示該字符串轉換爲整數時,採用的進制。而輸出的是十進制 # 而若是轉換大量的八進制字符串,每次都傳入int (x,base = 8)是比較麻煩的,爲此咱們能夠定義int8(x,base = 8) def int8(x,base = 8): return int(x,base) print u'八進制數:', int8('12') # 也可使用functools.partial,這樣就不用從新def定義新函數int8()了 int8 = functools.partial(int ,base = 8) print u'使用functools.partial的八進制數:',int8('15') # int8函數把參數從新設定默認值爲8 ,但也能夠在函數調用時傳入其餘值。例如:int8('15',base = 16) # 建立偏函數時,實際上能夠接收函數對象、*args 和**kw 這3個參數 # int8 = functools.partial(int ,base = 8)其實是固定了關鍵字參數的值爲8,等效於kw = {'base' : 8} int('15', **kw) # max_ex = functools.partial(max ,10,12)# 會把10,12做爲可變參數自動添加到右邊,max_ex(1,2,3)等效於max(1,2,3,10,12)
運行效果:字符串
★當函數的參數個數太多,使用functools.partial 能夠建立一個新的函數,這個新函數能夠把原函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,從而在調用時更簡單