Python *args 與 **kwargs
*args 和 **kwargs 讓函數可以接受不定數量的參數,增加函數的彈性。
*args - 可變數量位置參數
*args 會將所有額外的位置參數收集成一個 tuple:
def add(*args):
print(f"args 的型別: {type(args)}")
print(f"args 的內容: {args}")
return sum(args)
print(add(1, 2)) # 3
print(add(1, 2, 3, 4, 5)) # 15
print(add()) # 0
輸出:
args 的型別: <class 'tuple'>
args 的內容: (1, 2)
3
args 的型別: <class 'tuple'>
args 的內容: (1, 2, 3, 4, 5)
15
args 的型別: <class 'tuple'>
args 的內容: ()
0
搭配一般參數
def greet(greeting, *names):
for name in names:
print(f"{greeting}, {name}!")
greet("Hello", "Alice", "Bob", "Charlie")
輸出:
Hello, Alice!
Hello, Bob!
Hello, Charlie!
*args 必須放在一般位置參數之後。**kwargs - 可變數量關鍵字參數
**kwargs 會將所有額外的關鍵字參數收集成一個 dict:
def print_info(**kwargs):
print(f"kwargs 的型別: {type(kwargs)}")
print(f"kwargs 的內容: {kwargs}")
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="Taipei")
輸出:
kwargs 的型別: <class 'dict'>
kwargs 的內容: {'name': 'Alice', 'age': 25, 'city': 'Taipei'}
name: Alice
age: 25
city: Taipei
搭配一般參數
def create_user(username, **details):
user = {"username": username}
user.update(details)
return user
user = create_user("alice", email="alice@example.com", age=25)
print(user)
# {'username': 'alice', 'email': 'alice@example.com', 'age': 25}
同時使用 *args 和 **kwargs
參數順序:一般參數 → *args → 關鍵字參數 → **kwargs
def example(a, b, *args, option=True, **kwargs):
print(f"a: {a}")
print(f"b: {b}")
print(f"args: {args}")
print(f"option: {option}")
print(f"kwargs: {kwargs}")
example(1, 2, 3, 4, 5, option=False, x=10, y=20)
輸出:
a: 1
b: 2
args: (3, 4, 5)
option: False
kwargs: {'x': 10, 'y': 20}
解包 (Unpacking)
用 * 解包序列
def add(a, b, c):
return a + b + c
numbers = [1, 2, 3]
print(add(*numbers)) # 6
# 等同於
print(add(1, 2, 3)) # 6
用 ** 解包字典
def greet(name, greeting):
print(f"{greeting}, {name}!")
data = {"name": "Alice", "greeting": "Hello"}
greet(**data) # Hello, Alice!
# 等同於
greet(name="Alice", greeting="Hello")
合併序列和字典
# 合併 list
list1 = [1, 2, 3]
list2 = [4, 5, 6]
merged = [*list1, *list2]
print(merged) # [1, 2, 3, 4, 5, 6]
# 合併 dict
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = {**dict1, **dict2}
print(merged) # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
實際應用
包裝函數 (Wrapper)
def log_call(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"Result: {result}")
return result
return wrapper
@log_call
def add(a, b):
return a + b
add(3, 5)
輸出:
Calling add with args=(3, 5), kwargs={}
Result: 8
傳遞參數給父類別
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
class Dog(Animal):
def __init__(self, breed, *args, **kwargs):
super().__init__(*args, **kwargs)
self.breed = breed
dog = Dog("Labrador", name="Buddy", age=3)
print(f"{dog.name} is a {dog.breed}") # Buddy is a Labrador
建立彈性的 API
def make_request(url, method="GET", **options):
print(f"{method} {url}")
for key, value in options.items():
print(f" {key}: {value}")
make_request(
"https://api.example.com/users",
method="POST",
headers={"Content-Type": "application/json"},
timeout=30,
data={"name": "Alice"}
)
建立設定物件
def create_config(required_setting, **optional_settings):
config = {
"required": required_setting,
"debug": False,
"timeout": 30,
"retries": 3
}
config.update(optional_settings)
return config
config = create_config("my_app", debug=True, timeout=60)
print(config)
# {'required': 'my_app', 'debug': True, 'timeout': 60, 'retries': 3}
命名慣例
args 和 kwargs 只是慣例名稱,你可以用任何名稱,但使用這些名稱可以讓程式碼更容易理解:
# 這樣也可以,但不建議
def func(*numbers, **options):
pass
# 建議使用標準名稱
def func(*args, **kwargs):
pass