python *和** 的作用?两个的去呗
在 Python 中,* 和 ** 的作用非常丰富,根据使用场景不同,含义也完全不同。我来帮你梳理清楚它们的区别和用法。
一、* 和 ** 的核心区别
使用场景 * (单星号) ** (双星号) 函数定义中 收集多余的位置参数为元组 (tuple) 收集多余的关键字参数为字典 (dict) 函数调用中 解包可迭代对象为位置参数 解包字典为关键字参数 赋值语句中 解包可迭代对象给多个变量 无此用法 字面量中 解包可迭代对象到列表/集合/元组中 解包字典到字典或函数调用中
二、详细用法示例
1. 在函数定义中:参数收集
*args – 收集多余的位置参数
def func(a, b, *args):
print(f"a={a}, b={b}")
print(f"args={args}") # args 是一个元组
func(1, 2, 3, 4, 5)
# 输出:
# a=1, b=2
# args=(3, 4, 5)
**kwargs – 收集多余的关键字参数
def func(a, b, **kwargs):
print(f"a={a}, b={b}")
print(f"kwargs={kwargs}") # kwargs 是一个字典
func(1, 2, x=100, y=200, z=300)
# 输出:
# a=1, b=2
# kwargs={'x': 100, 'y': 200, 'z': 300}
组合使用
def func(a, b, *args, **kwargs):
print(f"a={a}, b={b}")
print(f"args={args}")
print(f"kwargs={kwargs}")
func(1, 2, 3, 4, 5, x=100, y=200)
# 输出:
# a=1, b=2
# args=(3, 4, 5)
# kwargs={'x': 100, 'y': 200}
2. 在函数调用中:解包参数
* – 解包可迭代对象为位置参数
def add(a, b, c):
return a + b + c
nums = [1, 2, 3]
result = add(*nums) # 等价于 add(1, 2, 3)
print(result) # 输出: 6
# 也可以用在 range 等函数中
numbers = [5, 10]
r = range(*numbers) # 等价于 range(5, 10)
print(list(r)) # 输出: [5, 6, 7, 8, 9]
** – 解包字典为关键字参数
def greet(name, age, city):
return f"{name} is {age} years old, from {city}"
person = {"name": "Alice", "age": 30, "city": "Beijing"}
result = greet(**person) # 等价于 greet(name="Alice", age=30, city="Beijing")
print(result) # 输出: Alice is 30 years old, from Beijing
3. 在赋值语句中:解包(Python 3.5+)
这是 Python 3 非常有用的特性,常用来处理序列:
# 基本解包 first, *middle, last = [1, 2, 3, 4, 5] print(first) # 输出: 1 print(middle) # 输出: [2, 3, 4] print(last) # 输出: 5 # 丢弃中间部分 first, *_, last = [1, 2, 3, 4, 5] # _ 通常用作丢弃的变量名 print(first) # 输出: 1 print(last) # 输出: 5 # 字符串解包 head, *tail = "Python" print(head) # 输出: P print(tail) # 输出: ['y', 't', 'h', 'o', 'n']
4. 在字面量中:解包合并
Python 3.5+ 支持在列表、元组、集合中使用 *
# 合并列表 list1 = [1, 2, 3] list2 = [4, 5, 6] combined = [*list1, *list2] # 等价于 [1,2,3,4,5,6] print(combined) # 输出: [1, 2, 3, 4, 5, 6] # 合并多个可迭代对象 combined2 = [*range(3), *"abc", *[7,8,9]] print(combined2) # 输出: [0, 1, 2, 'a', 'b', 'c', 7, 8, 9]
Python 3.5+ 支持在字典中使用 **
# 合并字典
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = {**dict1, **dict2} # 等价于 {"a":1, "b":2, "c":3, "d":4}
print(merged) # 输出: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# 带覆盖的合并
base = {"name": "Alice", "age": 30}
update = {"age": 31, "city": "Beijing"}
merged = {**base, **update} # 后面的覆盖前面的
print(merged) # 输出: {'name': 'Alice', 'age': 31, 'city': 'Beijing'}
5. 在 Pydantic 等库中的特殊用法
在你之前看到的代码中,这是 Pydantic 等库常见的模式:
# 在 Pydantic 模型中使用
user = User(**external_data) # 将字典解包为关键字参数传给构造函数
# 在函数参数中收集额外字段
def render_single_pdf(data: dict, out_path: str, **kwargs):
# **kwargs 会收集所有额外的关键字参数
# 然后可以用 {**data, **kwargs} 合并
merged_data = {**data, **kwargs}
rml = tpl.render(**merged_data) # 解包 merged_data 为模板参数
三、快速记忆口诀
符号 函数定义中 函数调用中 字面量中 * 收包(收成元组) 拆包(拆可迭代对象) 拆开放到列表里 ** 收包(收成字典) 拆包(拆字典) 拆开放到字典里
四、常见面试题
def foo(a, b, *args, c=10, **kwargs):
print(a, b, args, c, kwargs)
foo(1, 2, 3, 4, 5, c=20, d=30, e=40)
# 输出: 1 2 (3, 4, 5) 20 {'d': 30, 'e': 40}
# 解释:
# - 1,2 给 a,b
# - 3,4,5 被 *args 收集为元组 (3,4,5)
# - c=20 是命名关键字参数
# - d=30, e=40 被 **kwargs 收集为字典 {'d':30, 'e':40}
这就是 Python 中 * 和 ** 的所有主要用法。它们在处理不定长参数、解包数据、合并集合时非常有用,是 Python 灵活性的重要体现。

