写函数时经常遇到这种场景:调用方传的参数个数不固定——有时传两个,有时传五个,甚至一个都不传。比如写个日志函数,想让它能一口气记下任意多条消息;或者做个求和工具,支持 add(1)、add(1, 2, 3)、add(10, 20, 30, 40, 50)……这时候,硬编码形参就太死板了,得靠「可变参数传递」来解围。
星号 *:接收多余的位置参数
在函数定义里加一个 *args,就能把多余的「位置参数」打包成一个元组:
def print_all(*args):
print(f"共收到 {len(args)} 个参数:{args}")
for i, item in enumerate(args, 1):
print(f"第{i}个:{item}")试试看:print_all("苹果", "香蕉", "橙子") 输出:
共收到 3 个参数:('苹果', '香蕉', '橙子')
第1个:苹果
第2个:香蕉
第3个:橙子
注意:*args 必须放在普通参数之后、**kwargs 之前。比如 def func(a, b, *args, c=10) 是合法的;但 *args 不能出现在 c=10 后面。
双星号 **:接收多余的关键词参数
如果调用时用了关键字形式,比如 save(name="小明", age=28, city="杭州"),而你又不确定后面还会加哪些字段,那就上 **kwargs:
def save_user(**kwargs):
print("保存用户信息:")
for key, value in kwargs.items():
print(f" {key} = {value}")运行 save_user(name="小明", phone="138****1234", level="VIP"),就能自动拆出所有键值对,不用提前写死 name、phone、level 这些形参。
合体用法:*args + **kwargs
有些函数既要兼容位置参数,又要支持关键字参数,比如封装一个通用的 API 调用器:
def api_call(url, method="GET", *args, **kwargs):
print(f"请求 {method} {url}")
if args:
print(f"额外位置参数:{args}")
if kwargs:
print(f"请求头或参数:{kwargs}")调用示例:api_call("/login", "POST", "v2", timeout=5, headers={"Auth": "token123"})
输出清晰分层,位置参数归 args,关键字参数归 kwargs,互不干扰。
实际小技巧:解包也能用 *
可变参数不只是“收”,还能“发”。比如你有一堆数据存在列表里:data = [1, 2, 3],想直接传给 max() 函数,不用一个个写 max(data[0], data[1], data[2]),直接上星号解包:
data = [1, 2, 3]
print(max(*data)) # 输出 3同理,字典也能用 ** 解包传给函数:user = {"name": "阿强", "age": 31},然后 register(**user) 就等价于 register(name="阿强", age=31)。
可变参数不是炫技,而是让函数更灵活、更少改代码。下次再看到别人函数里带 *args 或 **kwargs,别慌,它只是在说:“来多少我都接着。”