【Python】 中的 * 与 **:Packing 与 Unpacking

Python中*的核心作用:打包/解包,以及一个特例:定义命名关键字参数

在函数中* 打包成tuple,而 ** 打包成dict
*可以解包任何可迭代对象,而 ** 只能解包mapping类(以字典为代表:键是字符串且和参数名一致)。

1. 三类使用场景概览

在 Python 中,*** 的核心作用是 打包(packing)解包(unpacking)

可以按"使用位置"分为三类:

  1. 函数定义阶段(Parameter Packing)

    • *args:位置参数打包成 tuple
    • **kwargs:关键字参数打包成 dict
  2. 函数调用阶段(Argument Unpacking)

    • *seq:序列解包成独立位置参数,可用于任何可迭代对象(包括tuple/list/set/字符串/range/生成器

      /自定义可迭代类)

    • **dict:字典解包成关键字参数

  3. 赋值语句中(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 三条规则

  1. 左侧只能出现 一个 *
  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]
相关推荐
Pyeako1 小时前
python网络爬虫
开发语言·爬虫·python·requsets库
qq_479875432 小时前
C++ 鸭子类型” (Duck Typing)
开发语言·c++
程序员杰哥2 小时前
如何用Postman做接口自动化测试?
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
xxxxxmy3 小时前
同向双指针(滑动窗口)
python·算法·滑动窗口·同向双指针
测试19983 小时前
selenium自动化测试详解
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
勇气要爆发3 小时前
【第一阶段—基础准备】第五章:Python模块和包管理(基础篇)—变形金刚的装备库
开发语言·python
lkbhua莱克瓦243 小时前
Java进阶——IO流
java·开发语言·笔记·学习方法·io流
阿杰同学3 小时前
Java中55种锁,高级面试题,最新面试题
java·开发语言