当前位置:首页>python>Python进阶:彻底搞懂*args和**kwargs是什么?

Python进阶:彻底搞懂*args和**kwargs是什么?

  • 2026-09-02 20:20:32
Python进阶:彻底搞懂*args和**kwargs是什么?

让函数拥有“无限容量”的秘密武器


在Python学习中,你是否见过这样的函数定义:

deffunc(*args, **kwargs):
pass

这个看起来有点奇怪的*args和**kwargs到底是什么?为什么有些函数要这样写?今天我们就用最直观的方式,彻底搞懂这对Python中的“万能接收器”。


一句话定义

*args和**kwargs允许函数接收可变数量的参数,让你的代码更灵活、更通用。

  • *args:接收任意多个位置参数,打包成元组(tuple)
  • **kwargs:接收任意多个关键字参数,打包成字典(dict)

直观示例:一个能“装下所有”的函数

看这个例子:

defgreet(*args, **kwargs):
# 处理位置参数
for name in args:
        print(f"Hi {name}")

# 处理关键字参数
for key, value in kwargs.items():
        print(f"{key} = {value}")

# 调用时传入任意参数
greet("Ana", "Bob", age=30, city="Lisbon")

输出:

Hi Ana
Hi Bob
age = 30
city = Lisbon

发生了什么?

  • "Ana"和"Bob"作为位置参数,被*args收集成元组 ("Ana", "Bob")
  • age=30和city="Lisbon"作为关键字参数,被**kwargs收集成字典 {"age": 30, "city": "Lisbon"}

拆解语法:*和**的作用

1. 在函数定义中:打包参数

defshow(*args, **kwargs):
    print("args:", args)      # 元组
    print("kwargs:", kwargs)  # 字典

show(1, 2, 3, a=4, b=5)
# 输出:
# args: (1, 2, 3)
# kwargs: {'a': 4, 'b': 5}
  • *args把位置参数打包成元组
  • **kwargs把关键字参数打包成字典

2. 在函数调用中:解包参数

defadd(a, b, c):
return a + b + c

numbers = [1, 2, 3]
print(add(*numbers))  # 解包列表,等价于 add(1, 2, 3)
# 输出:6

info = {"a": 4, "b": 5, "c": 6}
print(add(**info))    # 解包字典,等价于 add(a=4, b=5, c=6)
# 输出:15
  • *iterable解包可迭代对象为位置参数
  • **dict解包字典为关键字参数

为什么叫args和kwargs?

这只是约定俗成的命名,并非语法强制:

  • args= arguments(参数们)
  • kwargs= keywordarguments(关键字参数们)

你也可以用任何名字,关键是前面的*和**:

deffunc(*任何名字, **随便什么):
# 仍然有效

但为了代码可读性,请永远遵守约定,使用*args和**kwargs。


实际应用场景

场景1:装饰器——最经典的应用

deftimer(func):
defwrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)  # 原封不动传递参数
        print(f"耗时:{time.time() - start}秒")
return result
return wrapper

@timer
defslow_add(a, b):
    time.sleep(1)
return a + b

slow_add(3, 5)  # 装饰器能处理任意参数的函数

场景2:子类调用父类方法

classChild(Parent):
def__init__(self, *args, **kwargs):
# 先让父类处理它需要的参数
        super().__init__(*args, **kwargs)
# 再处理子类特有的逻辑
        self.extra = "额外属性"

场景3:编写通用接口

defflexible_api(endpoint, *args, **kwargs):
"""通用的API调用函数,可处理不同接口的参数差异"""
    print(f"调用 {endpoint}")
    print(f"位置参数:{args}")
    print(f"关键字参数:{kwargs}")
# 这里可以统一处理日志、缓存、重试等逻辑

常见误区

误区1:混淆*args和**kwargs的顺序

defcorrect(a, b, *args, c=10, **kwargs):
pass# 正确:位置参数 → *args → 默认参数 → **kwargs

defwrong(*args, a, b):
pass# 错误:*args之后不能再有普通位置参数

误区2:以为args和kwargs是关键词

# 这是错误的!*和**才是关键
deffunc(args, kwargs):# 普通的两个参数,不是可变参数
pass

误区3:过度使用

# 避免这样:明明知道需要哪些参数,却用*args/**kwargs
defcalculate(*args):
# 必须手动检查args长度,容易出错
return args[0] + args[1] * args[2]

# 应该这样:明确参数意图
defcalculate(x, y, z):
return x + y * z

何时使用?一张表说清楚

场景
是否用*args/**kwargs
原因
函数参数完全确定
❌ 不用
明确参数名让代码自文档化
装饰器
✅ 必须用
需要适配任意被装饰函数
包装/代理函数
✅ 推荐用
透明传递所有参数
参数数量不确定
✅ 必须用
如print()、max()
子类扩展父类
✅ 推荐用
父类可能更新参数

总结:记住三点

  1. *args收集位置参数为元组,**kwargs收集关键字参数为字典
  2. *和**既可用于定义函数(打包),也可用于调用函数(解包)
  3. 这是Python实现灵活接口、装饰器、类继承的基石

现在,当你再看到*args, **kwargs时,你应该知道:这是一个准备拥抱任何可能性的函数。


一句话记忆:*args收位置,**kwargs收键值,让函数容量无限大。

互动提问:你在写代码时遇到过需要可变参数的场景吗?欢迎在评论区分享你的用例!

贝叶斯学派与频率学派如何共塑现代数据分析
一张图看懂机器学习:从入门到高手的完整学习路线图
用Python一键生成3D地形图:让你的数据“站”起来

最新文章

随机文章