Python函数参数解包详解:*与**的调用、定义及常见误区
本文详细讲解 Python 中单星号 * 与双星号 ** 在函数调用和定义中的核心作用。通过位置参数与关键字参数的展开机制、容器合并技巧、强制关键字参数规则以及常见类型错误排查,帮助开发者掌握 Python 高级传参语法,提升代码简洁性与可维护性。
核心概念:参数解包的两种形态
在 Python 函数调用时,* 和 ** 用于将容器内的元素拆解并传递给目标函数。单星号针对序列对象(如列表、元组),生成位置参数;双星号针对字典对象,生成关键字参数。
| 符号 | 解包对象 | 调用时变为 |
|---|---|---|
* | 列表、元组等序列 | 位置参数 func(1, 2, 3) |
** | 字典 | 关键字参数 func(a=1, b=2) |
记忆口诀:一个星号管位置,两个星号管关键字。
单星号 *:展开序列与收集多余参数
调用时:将列表或元组拆分为位置参数
def greet(name, age, city):
print(f"{name}, {age}岁, 来自{city}")
args = ["Alice", 20, "Beijing"]
greet(*args) # 等价于 greet("Alice", 20, "Beijing")
*args 会按顺序将容器元素匹配到函数的位置形参上。元组操作方式完全一致:
point = (10, 20)
def draw(x, y):
print(x, y)
draw(*point) # 输出: 10 20
混用普通参数时的排列规则
在函数调用中,* 解包段通常位于其他位置参数之后,且插入位置会直接改变参数匹配顺序。
# 正确写法
foo(1, *[2, 3]) # ✅ 等价于 foo(1, 2, 3)
# 错误写法示例
# foo(*[1, 2], 3) # ❌ SyntaxError: 星号表达式不允许出现在该位置
定义时:使用 *args 收集冗余位置参数
def foo(*args):
print(args)
print(type(args))
foo(1, 2, 3)
# 输出:
# (1, 2, 3)
# <class 'tuple'>
当传入的参数数量超过明确定义的形参时,未被捕获的值会自动打包为元组赋予 *args。
双星号 **:字典展开与关键字参数收集
调用时:将字典键值对映射为关键字参数
def greet(name, age):
print(f"name={name}, age={age}")
info = {"name": "Alice", "age": 20}
greet(**info) # 等价于 greet(name="Alice", age=20)
字典的 key 必须严格对应函数的形参名,且必须是合法的 Python 标识符。
定义时:使用 **kwargs 收集冗余关键字参数
def foo(**kwargs):
print(kwargs)
foo(a=1, b=2) # 输出: {'a': 1, 'b': 2}
工程实战中的应用链
在配置类实例化或数据过滤场景中,常结合字典推导式使用:
values = {"max_step_num": 5, "locale": "", "debug": False}
# 过滤空值后解包传给配置类
return cls(**{k: v for k, v in values.items() if v})
- 字典推导式筛选出有效配置项
**展开为Configuration(max_step_num=5, debug=False)- 未提供的字段由类的默认值自动补全
混合使用与高级特性
调用时同时解包位置与关键字参数
pos = [3, 4]
kw = {"x": 10, "y": 20}
def foo(a, b, c, x, y):
print(a, b, c, x, y)
foo(1, 2, *pos, **kw) # 等价于 foo(1, 2, 3, 4, x=10, y=20)
调用时的固定执行顺序为:显式位置参数 → *解包 → 显式关键字参数 → **解包。
强制关键字参数(Python 3+)
在参数列表中单独放置一个 *,其后的所有形参必须通过关键字形式传递,否则触发 SyntaxError:
def connect(host, port, *, timeout=30, ssl=True):
pass
connect("localhost", 3306, timeout=60) # ✅
# connect("localhost", 3306, 60) # ❌ timeout 必须显式声明
该机制与 @dataclass(kw_only=True) 的设计哲学一致,强制要求命名传参以提升接口清晰度。
容器合并与高阶转发技巧
列表与元组合并
a = [1, 2]
b = [3, 4]
c = [*a, *b] # 结果: [1, 2, 3, 4]
字典合并与覆盖规则
a = {"x": 1}
b = {"y": 2}
c = {**a, **b} # 结果: {"x": 1, "y": 2}
b = {"x": 99}
{**a, **b} # 结果: {"x": 99} (后者优先覆盖)
装饰器与包装器参数转发
def wrapper(*args, **kwargs):
print("调用前准备...")
result = original_func(*args, **kwargs) # 原样透传
print("调用结束清理...")
return result
常见报错与排错指南
参数数量不匹配
def foo(a, b): pass
foo(*[1, 2, 3]) # TypeError: too many positional arguments
foo(**{"a": 1}) # TypeError: missing 1 required positional argument: 'b'
关键字名称非法或重复
foo(**{"max-step": 5}) # KeyError: key含非字母字符,无法转为参数名
foo(1, b=2, *[3]) # TypeError: multiple values for argument 'b'
foo(1, **{"a": 99}) # TypeError: got multiple values for argument 'a'
运算符上下文混淆
注意 * 在不同语法域的含义差异:在表达式中是乘法运算(如 2 * 3 → 6),仅在函数调用的实参位置或函数定义的形参表头中,才表示解包或收集。
总结
foo(*[1, 2, 3]) # 位置参数展开: foo(1, 2, 3)
foo(**{"a": 1, "b": 2}) # 关键字参数展开: foo(a=1, b=2)
cls(**{"max_step_num": 5}) # 配置类注入: Configuration(max_step_num=5)
| 场景 | * 的行为 | ** 的行为 |
|---|---|---|
| 调用传参 | 将序列拆分为位置参数 | 将字典拆分为关键字参数 |
| 定义接收 | *args 收拢为 tuple | **kwargs 收拢为 dict |
以上就是Python利用*与**进行解包的详细内容,更多关于Python函数参数传递的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













