第18/19條 位置參數/關鍵字參數的應用

令函數接收可變的位置參數*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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章