Python中
*的核心作用:打包/解包,以及一个特例:定义命名关键字参数在函数中
*打包成tuple,而**打包成dict
*可以解包任何可迭代对象,而**只能解包mapping类(以字典为代表:键是字符串且和参数名一致)。
1. 三类使用场景概览
在 Python 中,* 和 ** 的核心作用是 打包(packing) 与 解包(unpacking)。
可以按"使用位置"分为三类:
-
函数定义阶段(Parameter Packing)
*args:位置参数打包成 tuple**kwargs:关键字参数打包成 dict
-
函数调用阶段(Argument Unpacking)
-
*seq:序列解包成独立位置参数,可用于任何可迭代对象(包括tuple/list/set/字符串/range/生成器/自定义可迭代类)
-
**dict:字典解包成关键字参数
-
-
赋值语句中(starred assignment)
a, *rest = seq
2. 赋值语句中(starred assignment)
2.1 基础解包
starred assignment target属于unpacking的一部分
Python 先"解包"整个序列;然后把未分配的部分"收集(collect)"起来作为
list赋给 rest。
python
a, b, c = ("苹果", "香蕉", "橘子")
2.2 使用 * 收集剩余元素
python
first, *rest = ("苹果", "香蕉", "橘子", "葡萄")
print(first) # 苹果
print(rest) # ['香蕉', '橘子', '葡萄']
2.3 三条规则
- 左侧只能出现 一个
* *可以位于任意位置*打包之后得到的是 列表
另一个示例:
python
first, *rest, last = ("苹果", "香蕉", "橘子", "葡萄")
print(first)
print(rest)
print(last)
3. 函数参数中的 Packing(定义阶段)
3.1 *args:位置参数打包(可变参数)
顾名思义,参数可变,这里主要体现在参数数量可变,符合位置的多个参数都可以被一起打包
python
def pack(*fruits):
print("打包成果篮:", fruits)
调用:
python
pack("苹果", "香蕉", "橘子")
输出:
打包成果篮: ('苹果', '香蕉', '橘子')
4. 函数调用中的 Unpacking(调用阶段)
4.1 如果不解包
python
basket = ["苹果", "香蕉", "橘子"]
pack(basket)
输出:
(['苹果', '香蕉', '橘子'], ) #列表被当做整体传入
4.2 使用 * 解包
python
pack(*basket)
输出:
('苹果', '香蕉', '橘子') #列表先被拆解再传入
5. Packing 与 Unpacking 组合使用
python
def pack(*fruits):
print("打包成果篮:", fruits)
return fruits
def sort_and_repack(*fruits):
print("收到水果数量:", len(fruits))
first, *rest = fruits
print(f"The first is:{first}, the rests are{rest}")
return pack(*rest)
调用:
python
sort_and_repack("苹果", "香蕉", "橘子", "葡萄")
输出:
收到水果数量: 4
The first is:苹果, the rests are['香蕉', '橘子', '葡萄']
打包成果篮: ('香蕉', '橘子', '葡萄')
流程解读
| 步骤 | 代码片段 | Packing / Unpacking | 说明 |
|---|---|---|---|
| 1 | *fruits 参数 |
Parameter Packing | 收集所有入参形成元组 |
| 2 | first, *rest = fruits |
Iterable Unpacking | 拆成"第一个 + 被打包的其余部分列表" |
| 3 | pack(*rest) |
Argument Unpacking | 解包列表(被打包的其余部分)为多个独立参数 |
| 4 | def pack(*fruits) |
Parameter Packing | 再次打包多个参数为元组 |
6. 关键字参数与关键字-only 参数(命名关键字参数)
6.1 什么是关键字参数(keyword arguments)
调用参数时使用 名称=值 的方式:
python
def move(x, y):
print(x, y)
move(y=10, x=3)
关键字参数顺序不重要,因为要指定名称。
6.2 关键字-only 参数(keyword-only parameters)
又叫命名关键字参数
其实记忆成"keyword-only参数"好像更有助于理解,因为其强制必须使用关键词输入参数
*用于定义keyword-only参数是一种特例,和打包/解包无关,这里引入是为了避免混淆
定义方式有两种:
6.2.1 使用独立 * 分隔
python
def save(filename, *, backup=True, compress=False):
print(filename, backup, compress)
调用:
python
save("data.txt", backup=False) # 正确
save("data.txt", False, True) # 错误
6.2.2 在 *args 后定义keyword-only 参数
python
def report(title, *values, detail=False, export=None):
print("title:", title)
print("values:", values)
print("detail:", detail)
print("export:", export)
调用:
python
report("统计结果", 1, 2, 3, detail=True, export="result.csv")
错误示例:
python
report("统计结果", 1, 2, 3, True)
# TypeError: got multiple values for argument 'detail'
6.3 为什么需要 keyword-only 参数
- 强制用户写清楚参数名,提升可读性
- 避免误传位置参数
- 常用于逻辑开关型参数,例如
compress=False
7. **kwargs:关键字参数的打包与解包
7.1 定义阶段:**kwargs(关键字参数打包)
python
def collect(**fruits):
print("收到带标签的水果:", fruits)
collect(苹果="红", 香蕉="黄")
输出:
{'苹果': '红', '香蕉': '黄'}
所有
键=值被打包成字典。
7.2 调用阶段:**dict(关键字参数解包)
其实
**解包所有的对 mapping 类型对象,最高频使用的就是dict
python
def show(a, b, c):
print(a, b, c)
basket = {"a": "苹果", "b": "香蕉", "c": "葡萄"}
show(**basket)
等价于:
python
show(a="苹果", b="香蕉", c="葡萄")
字典basket被拆解
8. 函数参数的五大类与推荐顺序
8.1 五大类参数
| 类型 | 示例 | 含义 |
|---|---|---|
| 位置参数 | a, b |
按顺序传入 |
| 默认参数 | b=10 |
默认值可省略 |
| 可变位置参数 | *args |
打包为 tuple |
| 命名关键字参数 | *, x, y |
必须使用关键词传参 |
| 可变关键字参数 | **kwargs |
打包为 dict |
8.2 推荐顺序
位置参数 → 默认参数 → *args → 关键字-only → **kwargs
示例:
python
def full_example(a, b=2, *args, c=10, d=20, **kwargs):
print(a, b, args, c, d, kwargs)
full_example(1, 3, 4, 5, c=100, e=999)
输出:
1 3 (4, 5) 100 20 {'e': 999}
9. 常见错误与陷阱
9.1 忘记解包列表
python
basket = ["苹果", "香蕉"]
pack(basket) # 错误语义
pack(*basket) # 正确
9.2 可变默认参数陷阱
❎错误写法:
python
def bad(x, lst=[]):
lst.append(x)
return lst
#[]是一个可变参数,其作为默认参数会一直变化,导致结果不稳定
✅正确写法:
python
def good(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
9.3 冲突赋值
python
def f(a, b): ...
f(1, a=2) # a被重复赋值
10. 总结
* / ** 在定义时 → 打包
* / ** 在调用时 → 解包
赋值中使用 * → 序列解包(iterable unpacking)
关键字-only 参数通过 * 隔离
避免使用可变默认参数
附:
* 和 ** 不仅仅用于(函数参数),还用于:
- 列表合并
- 字典合并
- 可迭代对象解构赋值
1. 列表合并(解包)
python
a = [1,2]
b = [3,4]
c = [*a, *b]
2. 字典合并(解包)
python
d = {**{"x":1}, **{"y":2}}
3. 结构赋值(解包)
python
first, *rest = [1,2,3,4] # 解包为1和[2,3,4]