你有没有遇到过这种场景:写了一个函数,参数死板得像石头,非得按照顺序一个个传。如果临时想多加几个参数,立马报错。于是你只能不停改函数定义,改着改着自己都乱了。
其实 Python 早就给了我们一个神器,专门解决这种"参数不确定"的尴尬场景------那就是 *args 和 **kwargs。别被这两个名字吓到,它们的本质其实就是两个袋子:
- 一个袋子专门装位置参数(*args → 元组);
- 另一个袋子专门装关键字参数(**kwargs → 字典)。
只要学会用这两个袋子,你的函数立刻灵活到飞起。

1. 最直观的感受:把参数全都装起来
我们先看个最常见的例子:
python
def demo_args(a, b, *args, **kwargs):
print(f"标准参数: {a}, {b}")
print(f"位置参数 (*args): {args} (类型: {type(args)})")
print(f"关键字参数 (**kwargs): {kwargs} (类型: {type(kwargs)})")
demo_args(1, 2, 3, 4, 5, name="Alice", age=30)
输出结果一目了然:
- 前两个是标准参数;
- 额外的数字自动打包进
args
,形成一个元组; - 关键字参数则全都放进
kwargs
,成了字典。
是不是很像餐厅点菜?前两个是必点菜,剩下的都扔进大盘子里,不用提前规定数量。
2. 为什么说它是"解放双手"的神器?
来想象一个真实的场景:你写了一个计算器函数,有时候只想加法,有时候要算平均值,有时候还要控制小数点精度。如果不用 *args 和 **kwargs,你得写一堆重复函数。
看看这个版本:
python
def safe_calculator(op, *nums, **options):
# 验证数字类型
if not all(isinstance(n, (int, float)) for n in nums):
raise TypeError("必须输入数字")
# 处理选项
precision = options.pop('precision', 2) # 默认精度2
if options: # 检查未处理的关键字
raise ValueError(f"无效选项: {', '.join(options.keys())}")
# 执行操作
if op == 'add':
result = sum(nums)
elif op == 'avg':
result = sum(nums) / len(nums)
else:
raise ValueError("未知操作")
return round(result, precision)
print(safe_calculator('avg', 1.5, 2, 3.7, precision=3)) # 2.400
注意几个亮点:
- 位置参数随便丢几个数字进去就能算;
- 精度控制这种额外需求,直接扔进
kwargs
; - 未来如果想扩展新功能,直接加关键字参数即可,完全不用改函数结构。
这就是 *args 和 **kwargs 的精髓:让函数保持开放扩展,而不是死板固定。
3. 代码复用的幕后英雄:装饰器和继承
很多人刚学 Python,觉得装饰器、继承这些高阶语法晦涩难懂。但只要你知道 *args 和 **kwargs,其实它们背后就是在做参数打包和转发。
(1)装饰器的例子
python
def debug_decorator(func):
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}({args}, {kwargs})")
result = func(*args, **kwargs)
print(f"返回结果: {result}")
return result
return wrapper
@debug_decorator
def multiply(a, b):
return a * b
multiply(5, b=6)
这里的 wrapper
就像一个"万能接口",不管传什么参数,它都能兜住,再原封不动转给真正的函数。这就是为什么装饰器能无缝给任何函数加功能。
(2)继承的例子
python
class Animal:
def __init__(self, name, legs):
self.name = name
self.legs = legs
class Bird(Animal):
def __init__(self, can_fly, *args, **kwargs):
super().__init__(*args, **kwargs) # 透传参数
self.can_fly = can_fly
parrot = Bird(can_fly=True, name="Polly", legs=2)
print(f"{parrot.name} 有 {parrot.legs} 条腿, 会飞: {parrot.can_fly}")
如果不用 *args 和 **kwargs,你就得在子类里重复写一堆父类的参数传递逻辑。现在只要透传一下,父类需要啥,它自动帮你装好送过去。这就是优雅。
4. 容易踩的坑:这几个错误一定要避开
别看 *args 和 **kwargs 灵活,但也有一些"暗礁",很多人第一次都会踩。
-
参数顺序不能乱正确顺序是:标准参数 → *args → 关键字参数 → **kwargs。 否则报错让你怀疑人生。
-
别重复赋值
pythondef problematic(x, **kwargs): print(x, kwargs) problematic(1, x=2) # 报错:x重复赋值
-
调用时顺序要对 位置参数要放前面,关键字参数在后面,不然会直接
SyntaxError
。
5. 实用小技巧:写代码更优雅
强制关键字参数
python
def safe_divide(numerator, denominator, *, ignore_zero=False):
if denominator == 0:
return float('inf') if ignore_zero else None
return numerator / denominator
print(safe_divide(5, 0)) # None
print(safe_divide(5, 0, ignore_zero=True)) # inf
加一个 *
,后面参数必须写成关键字形式,避免调用时传错位置。
字典合并
arduino
def merge_config(defaults, **overrides):
return {**defaults, **overrides}
config = merge_config({"color": "red", "size": 10}, size=20, opacity=0.5)
# {'color': 'red', 'size': 20, 'opacity': 0.5}
API 参数转发
python
class Database:
def execute(self, query, *params, **options):
print(f"执行查询: {query}")
print(f"参数: {params}")
print(f"选项: {options}")
class UserService:
def __init__(self, db):
self.db = db
def get_user(self, user_id, use_cache=True):
return self.db.execute(
"SELECT * FROM users WHERE id = %s",
user_id,
timeout=10,
cache=use_cache
)
db = Database()
service = UserService(db)
service.get_user(123, use_cache=False)
看到了吗?转发参数就像"物流分拣中心",原封不动把数据派送给下一层。
6. 总结:*args 和 **kwargs 就是 Python 的"保险丝"
很多人写 Python 程序时,最怕的就是"未来需求变了"。今天只要两个参数,明天可能要十个。你不能每次都把函数拆了重写,这样太费劲。
而 *args 和 **kwargs,正是 Python 设计给你的灵活接口:
- *args → 位置参数打包成元组;
- **kwargs → 关键字参数打包成字典;
- 它们能让函数写得既简洁,又能轻松扩展;
- 常见应用:装饰器、继承、参数校验、API设计......
记住一句话:**当你不确定未来会传什么参数时,就放心交给 *args 和 kwargs。