python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python解包

Python利用*与**进行解包的完整教学

作者:为爱停留

这篇文章主要为大家详细介绍了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})
  1. 字典推导式 → 过滤得到有效配置
  2. ** → 展开成 Configuration(字段=值, ...)
  3. 未出现的字段 → 用 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}

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解包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文