Python利用*与**进行解包的完整教学
学习笔记:单星号 * 展开列表/元组为位置参数,双星号 ** 展开字典为关键字参数。
前言
在 Python 函数调用里,* 和 ** 都用来 「解包」——把容器里的元素拆开,传给函数。
| 符号 | 解包对象 | 变成 |
|---|---|---|
* | 列表、元组等序列 | 位置参数 func(1, 2, 3) |
** | 字典 | 关键字参数 func(a=1, b=2) |
记忆:一个星号管位置,两个星号管关键字。
一、单星号*— 展开序列
1.1 调用函数时:*展开列表/元组
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) # draw(10, 20)
1.2 和普通参数混用
def foo(a, b, c):
print(a, b, c)
foo(1, *[2, 3]) # foo(1, 2, 3)
foo(*[1, 2], 3) # ❌ 语法错误,* 解包段必须在最后或单独使用
foo(1, *[], 3) # ❌ 同上
正确混用:
rest = [2, 3] foo(1, *rest) # ✅ a=1, b=2, c=3
1.3 定义函数时:*args收集多余位置参数
def foo(*args):
print(args)
print(type(args))
foo(1, 2, 3)
# (1, 2, 3)
# <class 'tuple'>
| 场景 | * 的作用 |
|---|---|
调用时 func(*[1,2,3]) | 把序列 拆开 传入 |
定义时 def f(*args) | 把多出来的位置参数 收进 元组 |
1.4 解包其他序列
nums = range(1, 4) # range 对象
list(*nums) # ❌ range 不能直接 * 给 list()
print(*nums) # ✅ print(1, 2, 3)
def total(a, b, c):
return a + b + c
total(*[10, 20, 30]) # 60
二、双星号**— 展开字典
2.1 调用函数时:**展开 dict
def greet(name, age):
print(f"name={name}, age={age}")
info = {"name": "Alice", "age": 20}
greet(**info)
# 等价于 greet(name="Alice", age=20)
**info 把 dict 的每个 key: value 变成 key=value 关键字参数。
2.2 定义函数时:**kwargs收集多余关键字参数
def foo(**kwargs):
print(kwargs)
foo(a=1, b=2)
# {'a': 1, 'b': 2}
| 场景 | ** 的作用 |
|---|---|
调用时 func(**d) | 把 dict 拆开 传入 |
定义时 def f(**kwargs) | 把关键字参数 收进 dict |
2.3 项目里的真实用法
filtered = {"max_step_num": 5}
cls(**filtered)
# 等价于 Configuration(max_step_num=5)
完整链条(src/config/configuration.py):
return cls(**{k: v for k, v in values.items() if v})
- 字典推导式 → 过滤得到有效配置
**→ 展开成Configuration(字段=值, ...)- 未出现的字段 → 用 dataclass 默认值
三、*和**一起用
3.1 定义函数:*args+**kwargs
def foo(a, b, *args, **kwargs):
print("a, b =", a, b)
print("args =", args)
print("kwargs=", kwargs)
foo(1, 2, 3, 4, x=10, y=20)
# a, b = 1 2
# args = (3, 4)
# kwargs= {'x': 10, 'y': 20}
a, b:前两个位置参数*args:多出来的位置参数 → 元组**kwargs:所有关键字参数 → 字典
3.2 调用函数:同时解包
def foo(a, b, c, x, y):
print(a, b, c, x, y)
pos = [3, 4]
kw = {"x": 10, "y": 20}
foo(1, 2, *pos, **kw)
# foo(1, 2, 3, 4, x=10, y=20)
# 输出: 1 2 3 4 10 20
顺序规则(调用时):
位置参数 → *解包序列 → 关键字参数 → **解包字典
3.3 强制关键字参数(Python 3+)
定义里单独一个 * 表示:后面的参数必须用关键字传。
def connect(host, port, *, timeout=30, ssl=True):
...
connect("localhost", 3306, timeout=60) # ✅
connect("localhost", 3306, 60) # ❌ timeout 必须用 timeout=60
你们项目的 @dataclass(kw_only=True) 也是类似思路:创建对象时必须写 Configuration(max_step_num=3)。
四、*/**的其他用法(补充)
4.1 合并列表 / 元组
a = [1, 2] b = [3, 4] c = [*a, *b] # [1, 2, 3, 4]
4.2 合并字典
a = {"x": 1}
b = {"y": 2}
c = {**a, **b} # {"x": 1, "y": 2}
b = {"x": 99}
{**a, **b} # {"x": 99} 后面的覆盖前面的
4.3 函数传参转发(包装器常见写法)
def wrapper(*args, **kwargs):
print("调用前")
result = original_func(*args, **kwargs) # 原样转发
print("调用后")
return result
五、*vs**对照表
单星号 * | 双星号 ** | |
|---|---|---|
| 解包对象 | list、tuple、range 等序列 | dict |
| 调用时变成 | 位置参数 | 关键字参数 |
| 定义时收集 | *args → tuple | **kwargs → dict |
| 典型例子 | func(*[1, 2, 3]) | func(**{"a": 1}) |
| 元素要求 | 按顺序对应参数 | key 必须是合法参数名 |
六、常见误区
误区 1:参数个数对不上
def foo(a, b):
pass
foo(*[1, 2, 3]) # ❌ TypeError: too many arguments
foo(**{"a": 1}) # ❌ TypeError: missing b
误区 2:**的 key 不是合法标识符
foo(**{"max-step": 5}) # ❌ key 带横线,不能当参数名
foo(**{"max_step_num": 5}) # ✅
误区 3:重复传参
def foo(a, b):
pass
foo(1, *[2, 3]) # ✅
foo(1, b=2, *[3]) # ❌ b 传了两次
foo(1, **{"a": 99}) # ❌ a 传了两次
误区 4:混淆「解包」和「乘法」
2 * 3 # 乘法 → 6 func(*[2, 3]) # 解包 → 传入两个参数 2 和 3
上下文不同:在函数调用或定义参数列表里,* / ** 是解包;在表达式里是运算。
七、小练习
# 练习 1:用 * 解包
def add(a, b, c):
return a + b + c
print(add(*[1, 2, 3])) # 6
# 练习 2:用 ** 解包
def profile(name, age):
return f"{name}-{age}"
print(profile(**{"name": "Tom", "age": 18})) # Tom-18
# 练习 3:模拟项目
def fake_config(**kwargs):
print(kwargs)
fake_config(**{"max_step_num": 5, "locale": "zh-CN"})
八、小结
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 / **kwargs | 把多出来的参数 收进 容器 |
到此这篇关于Python利用*与**进行解包的完整教学的文章就介绍到这了,更多相关Python解包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Python GUI编程学习笔记之tkinter控件的介绍及基本使用方法详解
这篇文章主要介绍了Python GUI编程学习笔记之tkinter控件的介绍及基本使用方法,结合实例形式详细分析了Python GUI编程中tkinter控件的原理、用法及相关操作注意事项,需要的朋友可以参考下2020-03-03


最新评论