本文将系统阐述python中 ** 和 * 操作符的使用,列举常用示例。
1. 打包(Packing)操作
1.1 赋值语句中的打包(* → 列表)
# 总是生成列表,即使源是元组
first, *middle, last = 1, 2, 3, 4, 5
print(middle) # [2, 3, 4] ← 总是列表!
a, *b = (10, 20, 30)
print(b) # [20, 30] ← 元组源也变成列表!
# 空列表情况
x, *y = [1]
print(y) # [] ← 空列表
1.2 函数定义中的打包(*args → 元组)
# 总是生成元组,即使传入列表
def func(*args):
print(f"args类型: {type(args)}, 值: {args}")
func(1, 2, 3) # args类型: <class 'tuple'>, 值: (1, 2, 3)
func(*[1, 2, 3]) # args类型: <class 'tuple'>, 值: (1, 2, 3)
func([1, 2, 3]) # args类型: <class 'tuple'>, 值: ([1, 2, 3],)
# 这个代码其实左边是合理的写法,但是右边不是合法的python表达式
# *a = *[1, 2, 3]1.3 函数定义中的打包(**kwargs → 字典)
# 收集关键字参数为字典
def func(**kwargs):
print(f"kwargs类型: {type(kwargs)}, 值: {kwargs}")
func(a=1, b=2, c=3) # kwargs类型: <class 'dict'>, 值: {'a': 1, 'b': 2, 'c': 3}2. 解包(Unpacking)操作
2.1 函数调用中的解包(* → 任何可迭代对象)
def func(a, b, c):
return a + b + c
# 各种可迭代对象都可以解包
print(func(*[1, 2, 3])) # 列表 → 6
print(func(*(4, 5, 6))) # 元组 → 15
print(func(*"789")) # 字符串 → '789'(字符连接)
print(func(*range(3))) # range对象 → 0+1+2 → 3
# 解包生成器
gen = (x for x in [10, 20, 30])
print(func(*gen)) # 602.2 函数调用中的解包(** → 仅字典,键必须是字符串)
def func(name, age, city):
return f"{name} is {age} years old, lives in {city}"
# 字典解包为关键字参数
person = {"name": "Alice", "age": 25, "city": "Beijing"}
print(func(**person)) # Alice is 25 years old, lives in Beijing
# 键必须是字符串!
bad_dict = {1: 'Alice', 2: 25} # 不能解包,键不是字符串
# print(func(**bad_dict)) # TypeError: keywords must be strings
2.3 赋值语句中的解包(自动解包)
# 不需要显式使用 *
a, b, c = [1, 2, 3] # 自动解包
print(a, b, c) # 1 2 3
x, y, z = (4, 5, 6) # 元组自动解包
print(x, y, z) # 4 5 6
3. 混合使用场景
3.1 函数定义中的混合使用
def complex_func(a, b, *args, **kwargs):
print(f"a={a}, b={b}")
print(f"args={args} (类型: {type(args)})")
print(f"kwargs={kwargs} (类型: {type(kwargs)})")
complex_func(1, 2, 3, 4, 5, x=10, y=20)
# 输出:
# a=1, b=2
# args=(3, 4, 5) (类型: <class 'tuple'>)
# kwargs={'x': 10, 'y': 20} (类型: <class 'dict'>)3.2 字典合并(Python 3.5+)
# 使用 ** 解包字典进行合并
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = {**dict1, **dict2}
print(merged) # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# 后面的字典覆盖前面的键
dict3 = {"a": 100, "e": 5}
merged2 = {**dict1, **dict3}
print(merged2) # {'a': 100, 'b': 2, 'e': 5}3.3 列表/元组合并
# 使用 * 解包可迭代对象进行合并
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined_list = [*list1, *list2]
print(combined_list) # [1, 2, 3, 4, 5, 6]
combined_tuple = (*list1, *list2)
print(combined_tuple) # (1, 2, 3, 4, 5, 6)4. 特殊注意事项
4.1 语法限制
# ❌ 错误:赋值右边不能单独使用 *
lst = [1, 2, 3]
# a, b, c = * lst # SyntaxError
# ✅ 正确:需要形成完整表达式
a, b, c = *lst, # 注意逗号,创建元组
a, b, c = lst # 直接解包(推荐)4.2 只能使用在赋值或函数参数中
# ❌ 这些会报错
# result = *[1, 2, 3] # SyntaxError
# if *[True, False]: pass # SyntaxError
# ✅ 这些是允许的
result = [*[1, 2, 3]] # 在列表内解包
args = (*[1, 2, 3],) # 在元组内解包5. 实用技巧
5.1 提取文件路径各部分¶
path = "/home/user/documents/file.txt"
*folders, filename = path.split("/")
print(folders) # ['', 'home', 'user', 'documents']
print(filename) # file.txt5.2 处理不确定数量的返回值
def get_data():
return "success", {"data": [1, 2, 3]}, 200, "extra", "info"
status, data, code, *extras = get_data()
print(status) # success
print(extras) # ['extra', 'info']5.3 灵活的API包装器
def api_wrapper(endpoint, *args, **kwargs):
# 记录日志
print(f"调用 {endpoint},参数: {args},关键字参数: {kwargs}")
# 实际API调用(模拟)
return f"结果来自 {endpoint}"
# 可以接受任意参数
result1 = api_wrapper("/users", 1, 2, 3, fields="name,age")
result2 = api_wrapper("/posts", category="tech", limit=10)