在函數定義def func(*args, **kw) 時,
參數args 前有‘*’符號,表示該參數是可變參數,即參數的數量可以爲任意個;
參數kw 前有‘**’符號,表示該參數是關鍵字參數,即參數帶有key,且數量可以爲任意個;
在函數調用 func(*args, **kw) 時,
參數args 前有‘*’符號,表示會把args展開,把其中的每個參數傳入;args是一個list或tuple.
參數kw 前有‘**’符號,表示會把kw展開,把其中的每個key=value傳入;kw是一個dict.
def sum(x,y,z=10,**kw):
print kw
return x+y+z
nms = [1,2]
sts = {'a':1,'z':20}
print sum(*nms,**sts) #實際運行 sum(1,2,z=20,a=1),並組裝了kw={'a':1} #output: {'a':1} 23
裝飾器:是一個函數,該函數接收一個函數,對其處理(裝飾),並返回一個函數。
def log(text=''): #接收參數,以定製裝飾器
def login(func): #定義裝飾器
def wrapper(*args,**kw): #在下面的調用時,發生了這樣的參數傳遞: args=(3,5), kw={'city': 'bj', 'm': 3, 'nat': 'ch'}
print '\t%s\t'%text #在log函數調用時,已定製text爲 'add three #'
print args, kw #output:(3,5) {'city': 'bj', 'm': 3, 'nat': 'ch'}
print 'start %s()'%func.__name__ #因爲func是add,所以func.__name__是add
res = func(*args,**kw)
print '\tres is %s'%str(res) #發生的調用爲 add(3,5,m=3,city='bj',nat='ch'),並組裝了kw={'city':'bj','nat':'ch'}
print 'end %s()'%func.__name__ # res is 3+5+3+3+(3!=5)*2 = 16
return res
return wrapper
return login
@log('add three #') #在函數定義前使用@裝飾器,表示調用裝飾器對函數add進行裝飾,在這裏發生的操作爲 add = log('add three #')(add),
def add(x,y,z=3,m=5,**kw): #也即 add = login(add),也即有 add = wrapper,所以實際對add的調用是對wrapper的調用
print kw #這一點可以從 print add.__name__ 得到結果“wrapper” 可以驗證。
return x+y+z+m+(m!=5)*2
add(3,5,m=3,city='bj',nat='ch')
print add.__name__ #output: "wrapper"
從上面可以看到對add進行裝飾後,add.__name__ 的結果是“wrapper” ,即add的簽名發生了變化,若有依賴函數簽名的代碼,就會發生錯誤;
所以,一般需要保持被裝飾函數的簽名不變,這需要在裝飾器中(login函數中,wrapper函數定義的前一行)執行如下代碼:
@functools.wraps(func)
當然需要先執行 import functools,以導入functools模塊