Learning Objectives
- 位置引數(positional arguments):依順序對應參數
- 關鍵字引數(keyword arguments):依名稱對應參數
- 預設引數(default arguments):沒有提供時使用預設值
- 不固定個數引數(arbitrary arguments):*args 與 **kwargs
四種傳引數的方法
上一課的函數,呼叫時要照順序給齊每個引數。實際寫程式時常會遇到:參數很多記不住順序、 大部分情況都用同一個值、或事先不知道會傳進幾個值。Python 提供四種傳引數的方法:
| 方法 | 定義時 | 呼叫時 |
|---|---|---|
| 位置引數(必要引數) | def f(a, b): |
f(1, 2) |
| 關鍵字引數 | 同上,不需要特別寫法 | f(b=2, a=1) |
| 預設引數 | def f(a, b=10): |
f(1) 或 f(1, 5) |
| 不固定個數引數 | def f(*args, **kwargs): |
f(1, 2, 3, x=4) |
用語提醒:定義函數時小括號裡的名稱叫參數(parameter),呼叫時實際傳進去的值叫引數(argument)。
位置引數與關鍵字引數
每次呼叫時,看右邊 printinfo 框架裡的 name 與 age 各拿到什麼值。第 6 行把順序放反了,程式不會出錯,但結果是錯的;第 7 行用關鍵字引數,就不必在意順序。
預設引數
dbname 與 port 有預設值,呼叫時可以省略。看第 7 行:想跳過中間的 dbname、只改 port,就用關鍵字引數。
錯誤的函數呼叫
定義預設引數時,預設引數必須放在位置引數之後:def f(a, b=1): 可以,def f(a=1, b): 是語法錯誤。
呼叫時常見的三種錯誤:
printinfo() # 少了必要引數 tblname
printinfo(port=8888, 'person') # 關鍵字引數後面又接位置引數
printinfo('person', tblname='products') # tblname 被給了兩次
第二種在程式還沒開始執行時就會被發現(SyntaxError),整支程式都不會跑;
另外兩種要到執行那一行時才引發 TypeError,可以用 try/except 接住,下面的範例就是這樣示範。
看 TypeError 的訊息
每個 try 區塊裡都是一次錯誤的呼叫。注意錯誤發生時函數內容完全沒有執行,訊息會清楚說明是哪個參數出了問題,養成讀錯誤訊息的習慣很有幫助。
想一想:位置、關鍵字與預設值
下面的程式會印出什麼?
def area(length, width=1):
return length * width
print(area(5), area(5, 3), area(width=2, length=4))
已知 def area(length, width=1):,執行 area(width=15) 會發生什麼事?
下列哪一種函數定義是不合法的?
不固定個數引數:*args
參數名稱前面加一個星號 *,就能接收任意個位置引數,它們會被收集成一個元組。看右邊 vartuple 的型態;沒有多出來的引數時,它是空元組 ()。
**kwargs:收集關鍵字引數
兩個星號 ** 會把多出來的關鍵字引數收集成字典,引數名稱變成 key。最後兩行是反方向的用法:呼叫時寫 * 把序列拆開、寫 ** 把字典拆開,再當作引數傳進去。
陷阱:可變的預設值
預設值是在執行 def 那一刻只建立一次的。如果預設值是串列,每次沒傳 box 的呼叫都會共用同一個串列。看右邊:第二次呼叫 add_item 時,box 指向的還是第一次那個串列。安全的寫法是預設值用 None。
追蹤 *args、**kwargs 與預設值
下面的程式會印出什麼?
def f(a, *rest):
print(a, len(rest), rest)
f(1, 2, 3)
下面的程式會印出什麼?
def log(msg, history=[]):
history.append(msg)
return len(history)
log("a")
log("b")
print(log("c"))
實作:預設引數與 *args
完成兩個函數:greet(name, greeting='Hello') 印出像 Hello, Amy! 的問候;average(*nums) 傳回所有數字的平均,沒有傳入任何數字時傳回 0。
重點精華回顧
- 位置引數(必要引數):依順序對應參數,個數與順序要和定義一致。
- 關鍵字引數:呼叫時寫
參數名稱=值,不必在意順序;混用時位置引數要在前面。 - 預設引數:
def f(a, b=10):,沒提供時用預設值;預設引數必須定義在位置引數之後。 - 少給必要引數、同一個參數給兩次、引數太多,都會引發 TypeError。
*args把多出來的位置引數收集成元組;**kwargs把多出來的關鍵字引數收集成字典。- 呼叫時
*序列、**字典可以把資料拆開當引數傳入。 - 預設值只在
def時建立一次,不要用串列、字典這類可變物件當預設值,改用None。