你有没有遇到过这种场景:写了一个函数,参数死板得像石头,非得按照顺序一个个传。如果临时想多加几个参数,立马报错。于是你只能不停改函数定义,改着改着自己都乱了。
其实 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。