python *和** 的作用?两个的去呗

作者: adm 分类: python 发布时间: 2025-04-13

在 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 灵活性的重要体现。

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!