跳到主要内容

解包

Python 里的 *** 解包用法详解

基础用法

1. 列表(元组)解包:*

lst = [1, 2, 3]
a, b, c = lst # a=1, b=2, c=3

# *用于参数传递
def foo(a, b, c):
print(a, b, c)

foo(*lst) # 等价于 foo(1, 2, 3)

2. 字典解包:**

d = {'x': 10, 'y': 20}

def bar(x, y):
print(x + y)

bar(**d) # 等价于 bar(x=10, y=20)

3. 变量收集

a, *b, c = [1, 2, 3, 4, 5]
# a=1, b=[2,3,4], c=5

4. 合并/拆解容器

lst1 = [1, 2]
lst2 = [3, 4]
merged = [*lst1, *lst2] # [1, 2, 3, 4]

dict1 = {'a': 1}
dict2 = {'b': 2}
merged_dict = {**dict1, **dict2} # {'a': 1, 'b': 2}

*args vs **kwargs 深入对比

*args - 位置参数

接收/传递按顺序的参数

def add(a, b, c):
return a + b + c

# 调用方式1:正常传参
add(1, 2, 3)

# 调用方式2:用 * 解包列表/元组
args = (1, 2, 3)
add(*args) # 等同于 add(1, 2, 3)

**kwargs - 关键字参数

接收/传递带名字的参数

def greet(name, age):
print(f"{name}, {age}岁")

# 调用方式1:正常传参
greet(name="小明", age=18)

# 调用方式2:用 ** 解包字典
kwargs = {"name": "小明", "age": 18}
greet(**kwargs) # 等同于 greet(name="小明", age=18)

装饰器中的应用

为什么装饰器需要两个?

为了支持各种调用方式

def my_decorator(func):
def wrapper(*args, **kwargs): # 接收所有参数
print("调用前")
result = func(*args, **kwargs) # 原样传给原函数
print("调用后")
return result
return wrapper

@my_decorator
def example(a, b, name="默认"):
print(f"a={a}, b={b}, name={name}")

# 这些调用方式都能工作!
example(1, 2) # *args 接收
example(1, 2, name="自定义") # *args + **kwargs
example(a=1, b=2) # **kwargs 接收

直观对比

# 只用 *args - ❌ 不支持关键字参数
def wrapper(*args):
func(*args)

example(1, 2, name="test") # 报错!

# 同时用两个 - ✅ 万能
def wrapper(*args, **kwargs):
func(*args, **kwargs)

example(1, 2, name="test") # 正常工作

函数定义中的参数收集

接收任意数量的位置参数

def sum_all(*args):
return sum(args)

print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15

接收任意数量的关键字参数

def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")

print_info(name="张三", age=25, city="北京")
# name: 张三
# age: 25
# city: 北京

组合使用

def flexible_func(required, *args, **kwargs):
print(f"必需参数: {required}")
print(f"额外位置参数: {args}")
print(f"关键字参数: {kwargs}")

flexible_func(1, 2, 3, name="test", value=100)
# 必需参数: 1
# 额外位置参数: (2, 3)
# 关键字参数: {'name': 'test', 'value': 100}

总结

简单记忆

  • * 一个星:顺序参数(位置)
  • ** 两个星:命名参数(关键字)
  • 装饰器用两个:兼容所有调用方式

常见使用场景

  • *:序列解包(列表/元组)、接收任意位置参数
  • **:字典解包、接收任意关键字参数
  • 装饰器、函数包装器:*args, **kwargs 组合使用
  • 容器合并:[*list1, *list2]{**dict1, **dict2}