Python *args 與 **kwargs

Python *args 與 **kwargs

這篇教學將帶你認識函式參數的進階寫法:*args 接收不定數量的「位置引數」、**kwargs 接收不定數量的「關鍵字引數」。看過 print(1, 2, 3) 可以塞任意個值嗎?背後就是這個機制。

寫法接收什麼在函式內的型別
*args任意數量的位置引數Tuple
**kwargs任意數量的關鍵字引數Dictionary

argskwargs 只是慣例名稱,重點是 ***。)

*args:不定數量的位置引數

def total(*args):
    print(type(args), args)
    return sum(args)

print(total(1, 2))
print(total(1, 2, 3, 4, 5))

執行輸出:

<class 'tuple'> (1, 2)
3
<class 'tuple'> (1, 2, 3, 4, 5)
15

傳入的值全部被收進一個 Tuple,函式不用管呼叫端傳幾個。

**kwargs:不定數量的關鍵字引數

def build_profile(**kwargs):
    print(type(kwargs))
    for key, value in kwargs.items():
        print(f"{key}: {value}")

build_profile(name="小明", age=25, city="台北")

執行輸出:

<class 'dict'>
name: 小明
age: 25
city: 台北

參數順序規則

混用時順序固定為:一般參數 → *args → 預設參數 → **kwargs

def demo(a, *args, mode="normal", **kwargs):
    print(f"a={a}, args={args}, mode={mode}, kwargs={kwargs}")

demo(1, 2, 3, mode="fast", debug=True)

執行輸出:

a=1, args=(2, 3), mode=fast, kwargs={'debug': True}

呼叫端的 * 與 **:解包

*** 在「呼叫端」是反過來的用途——把 List/Tuple 或 dict 拆開來傳:

def add(a, b, c):
    return a + b + c

nums = [1, 2, 3]
print(add(*nums))          # 等同 add(1, 2, 3)

config = {"a": 10, "b": 20, "c": 30}
print(add(**config))       # 等同 add(a=10, b=20, c=30)

執行輸出:

6
60

口訣:定義端的 * 是「收集」,呼叫端的 * 是「拆開」

實用範例:包裝函式

*args, **kwargs 最常見的實戰用途,是寫「不管原函式長怎樣都能轉傳」的包裝(第 36 篇 Decorator 的基礎):

def log_call(func, *args, **kwargs):
    print(f"呼叫 {func.__name__},參數 {args} {kwargs}")
    return func(*args, **kwargs)

def add(a, b):
    return a + b

print(log_call(add, 3, b=5))

執行輸出:

呼叫 add,參數 (3,) {'b': 5}
8

常見錯誤

1. TypeError: got multiple values for argument

同一個參數同時被位置引數與關鍵字引數指定,例如 demo(1, a=2)。檢查呼叫端是否重複指定。

2. 順序寫錯

def f(**kwargs, *args) 直接 SyntaxError。記住順序:一般 → *args → 預設 → **kwargs

3. 忘記在呼叫端解包

add(nums) 是把整個 List 當成一個參數(報 TypeError 缺參數),要拆開傳就寫 add(*nums)

總結

定義端 *args 收位置引數成 Tuple、**kwargs 收關鍵字引數成 dict;呼叫端 *** 則是把序列與字典拆開來傳;順序是一般 → *args → 預設 → **kwargs。這個機制是之後 Decorator 的基礎。下一篇處理一個常被忽略的主題:變數作用域。

延伸閱讀