令函數接收可變的位置參數(*args
),能夠讓代碼更加清晰。
案例1:定義log
函數打印輸出信息,如果參數個數固定,那麼必須將參數打包成列表傳進去。
def log(message,values):
if not values:
print(message)
else:
values_str = ', ' .join(str(x) for x in values)
print("%s:%s"%(message,values_str))
if __name__=='__main__':
log('My numbers are',[1,3])
但是當沒有參數輸入時,也必須傳入一個空列表[]
,比較麻煩。爲此可以採用接收可變的位置參數。
def log(message,*values):
if not values:
print(message)
else:
values_str = ', '.join(str(x) for x in values)
print("%s:%s"%(message,values_str))
if __name__=='__main__':
log('My numbers are',[1,3,9])
Python對傳入的列表在*values
接收後,逐個解析,作爲其輸入的位置參數。
接收可變的位置參數,會帶來以下兩個問題:
第一個問題,變長參數在傳給函數時,總是要先轉化爲元組(tuple)。意味着,如果某個函數的*
操作符參數爲生成器,那麼當調用該函數時,Python就必須將該生成器完整的迭代一輪,並把生成器所生成的每一個值,都放入元組中,可能會帶來內存的大量消耗,導致程序崩潰。
def my_generator():
for i in range(10):
yield i
def my_func(*args):
print(args)
if __name__=='__main__':
it = my_generator()
my_func(*it)
輸出結果:
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
因此,只有我們確認輸入參數個數比較少時,才應該令函數接收*args
式的變長參數。
第二個問題,如果以後要給函數添加新的位置參數,那就必須修改原來調用該函數的那些舊代碼。若是隻給參數列表前方添加新的位置參數,而不更新現有的調用代碼,則會產生難以調試的錯誤。
def log(sequence,message,*values):
if not values:
print"%s:%s"%(sequence,message))
else:
values_str = ', '.join(str(x) for x in values)
print("%s:%s:%s"%(sequence,message,values_str))
if __name__ == '__main__':
log(1,"Favorites",7,33) #新代碼
log("Favorites",7,33) #舊代碼
問題在於,之前寫好的舊代碼將7
原本是values
的一部分,卻賦值給了message
,還不會產生錯誤,導致代碼異常很難追蹤。
爲了避免這種情況,應該使用只能以關鍵字形式指定的參數,來擴展這種接受*args
的函數。
下面介紹關鍵字參數。
Python的所有位置參數都可以按照關鍵字傳遞。
def remainder(number,divisor):
return number % divisor
if __name__=='__main__':
# 下面幾種方式等效
remainder(20,7)
remainder(number=20,divisor=7)
remainder(20,divisor=7)
remainder(divisor=7,number=20)
remainder(number=20,7)# error
需要注意的是:
- 位置參數必須在關鍵字參數之前。
- 每個參數只能指定一次
remainder(20,number=7) ## Error
使用關鍵字參數的優點如下:
1.可以使得函數調用者清晰的明確各傳入參數的意義。
2.可以在函數定義中提供默認值
。大部分情況下,函數調用者只需要使用這些默認值就夠了,若要特別地指定某個參數,則可以指定相應的關鍵字參數,這樣可以減少重複代碼,使得代碼變得簡潔。
3.提供了一種擴充函數參數的有效方式,使得擴充之後的函數依然能與原有的那些相兼容。
##注意位置參數在關鍵字參數之前
def flow_rate(weight_diff,time_diff,period=1,units_per_kg=1):
return ((weight_diff * units_per_kg)/time_diff) * period