Python中`*`和`**`是什么? 如何如此重要??
1. Python中*
1.1. Python中*
代表什么?
在 Python 中,*
主要用于以下两种情况:
-
接收任意数量的位置参数:在函数定义中,
*args
用来接收传递给函数的任意数量的位置参数,这些参数会被打包成一个元组。 -
解包可迭代对象:在函数调用时,
*
可以用来解包一个可迭代对象(如列表、元组等),并将其元素作为位置参数传递给函数。
1.2. Python中*
使用语法
1.2.1. 函数定义时使用*
def func(*args):
for arg in args:
print(arg)
*args
会把所有传递给函数的位置参数收集到一个元组 args
中。1.2.2. 函数调用时使用*
def func(a, b, c):
print(a, b, c)
args = (1, 2)
func(*args, c=3)
*args
解包元组中的元素,作为位置参数传递给函数。1.2.3. 用于变量赋值
a, *b, c = [1, 2, 3, 4, 5]
print(a) # 1
print(b) # [2, 3, 4]
print(c) # 5
*b
可以收集剩余的元素,赋值给变量 b
。1.3. Python中*
常见使用场景
1.3.1. 接收不定数量的位置参数
当一个函数需要接收多个位置参数时,可以使用 *
进行捕获。这样函数可以接收任意数量的位置参数,并将它们打包成一个元组。
语法:
def function_name(*args):
# args 是一个元组,包含所有传递的参数
pass
示例:
def add(*numbers):
return sum(numbers)
print(add(1, 2, 3)) # 输出 6
print(add(4, 5, 6, 7)) # 输出 22
在上述例子中,*numbers
会接收所有传递给 add()
函数的参数,numbers
会是一个元组,sum(numbers)
计算元组中的元素之和。
1.3.2. 解包参数传递给函数
在调用函数时,*
可以用来解包一个可迭代对象(如列表、元组),将其元素作为位置参数传递给函数。
语法:
function_name(*iterable)
示例:
def greet(name, age):
print(f"Hello {name}, you are {age} years old.")
person = ("Alice", 30)
greet(*person) # 解包元组并传递给函数
在这个例子中,*person
解包了 person
元组中的元素,并将它们作为位置参数传递给 greet()
函数。*person
的作用是将 person
元组的元素 ("Alice", 30)
依次传递给 greet()
函数中的 name
和 age
参数。
1.3.3. 用于拆解列表、元组或字典
*
不仅可以用于解包传递给函数的参数,还可以用于拆解列表、元组和字典,合并多个可迭代对象。它允许我们将多个集合对象的内容拆解并合并到一个新的集合中。
语法:
new_collection = [*collection1, *collection2]
示例:
list1 = [1, 2, 3]
list2 = [4, 5]
merged = [*list1, *list2] # 合并两个列表
print(merged) # 输出 [1, 2, 3, 4, 5]
在这个示例中,*list1
和 *list2
解包了两个列表,将它们的元素依次放入一个新的列表 merged
中。这样,多个列表或元组可以通过解包操作合并成一个新的集合。
如果要解包字典(合并字典),可以使用类似的方法:
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged_dict = {**dict1, **dict2} # 合并字典
print(merged_dict) # 输出 {'a': 1, 'b': 2, 'c': 3, 'd': 4}
1.3.4. 在函数参数中限制参数数量
*
也可以用来限制函数参数的传递方式,确保某些参数只能作为关键字参数传递,而不能作为位置参数。
语法:
def function_name(a, b, *, c):
# c 只能作为关键字参数传递
pass
示例:
def func(a, b, *, c):
print(a, b, c)
# 正确调用
func(1, 2, c=3) # 使用关键字参数调用
# 错误调用
# func(1, 2, 3) # 会抛出 TypeError,c 必须作为关键字参数传递
在此示例中,*
确保了参数 c
必须通过关键字方式传递,而不能作为位置参数传递。如果尝试将 c
作为位置参数传递(例如 func(1, 2, 3)
),会引发 TypeError
。
这种方式特别适用于当有多个可选参数时,我们希望强制用户使用关键字参数传递某些参数,以避免混淆。
总结
*
在函数定义中用来接收不定数量的位置参数。*
在函数调用中用来解包可迭代对象,将它们作为位置参数传递。*
可以用来合并列表或元组,解包集合中的元素。*
可以在函数参数中限定某些参数只能以关键字方式传递。2. Python中**
2.1. Python中**
代表什么?
在 Python 中,**
有两个主要作用:
-
接收不定数量的关键字参数:在函数定义中,
**kwargs
用于接收不定数量的关键字参数,这些参数会被打包成一个字典。 -
解包字典作为关键字参数传递给函数:在函数调用时,
**
用于解包一个字典,并将字典中的键值对作为关键字参数传递给函数。
2.2. Python中**
使用语法
2.2.1. 函数定义时使用**
在函数定义中,**kwargs
用于接收任意数量的关键字参数,kwargs
是一个字典,包含传递给函数的所有关键字参数。
语法:
def function_name(**kwargs):
# kwargs 是一个字典,包含所有传递的关键字参数
pass
示例:
def greet(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
greet(name="Alice", age=30, city="New York")
输出:
name: Alice
age: 30
city: New York
在此示例中,**kwargs
捕获了所有传递给函数的关键字参数,并将它们作为一个字典传递给函数。
2.2.2. 函数调用时使用**
在调用函数时,**
可以用来解包字典,将字典中的键值对作为关键字参数传递给函数。
语法:
function_name(**dict)
示例:
def greet(name, age, city):
print(f"Hello {name}, you are {age} years old, living in {city}.")
person = {"name": "Alice", "age": 30, "city": "New York"}
greet(**person)
输出:
Hello Alice, you are 30 years old, living in New York.
在这个示例中,**person
解包了字典中的键值对,并将它们作为关键字参数传递给 greet()
函数。
2.3. Python中**
常见使用场景
2.3.1. 接收不定数量的关键字参数
在函数中使用 **kwargs
语法,可以让函数接收不定数量的关键字参数。kwargs
是一个字典,包含所有传递给函数的关键字参数。这样,函数的参数可以动态地接受不同数量的关键字参数。
语法:
def function_name(**kwargs):
# kwargs 是一个字典,包含所有传递的关键字参数
pass
示例:
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=30, city="New York")
输出:
name: Alice
age: 30
city: New York
在这个示例中,**kwargs
接收了所有传递的关键字参数,将它们作为一个字典传递给函数 print_info
。然后,函数通过 items()
方法遍历并打印每个键值对。
2.3.2. 解包字典作为关键字参数传递给函数
当你有一个字典,并且希望将字典中的键值对作为关键字参数传递给函数时,可以使用 **
来解包字典。**
会将字典的键值对展开,并将每个键作为关键字参数传递给函数。
语法:
function_name(**dict)
示例:
def greet(name, age, city):
print(f"Hello {name}, you are {age} years old, living in {city}.")
person = {"name": "Alice", "age": 30, "city": "New York"}
greet(**person)
输出:
Hello Alice, you are 30 years old, living in New York.
在这个例子中,**person
解包了字典 person
中的键值对,并将它们作为关键字参数传递给 greet()
函数。name
, age
, 和 city
会被依次传递给函数。
2.3.3. 与*
结合使用:位置参数和关键字参数混合传递
可以在一个函数中同时使用 *args
和 **kwargs
,其中 *args
用于接收不定数量的位置参数,**kwargs
用于接收不定数量的关键字参数。这样,你可以在一个函数中同时处理位置参数和关键字参数。
语法:
def function_name(a, b, *args, **kwargs):
# a 和 b 是位置参数
# args 是可变位置参数(元组)
# kwargs 是可变关键字参数(字典)
pass
示例:
def greet(a, b, *args, **kwargs):
print(a, b)
print("Additional arguments:", args)
print("Keyword arguments:", kwargs)
greet(1, 2, 3, 4, name="Alice", age=30)
输出:
1 2
Additional arguments: (3, 4)
Keyword arguments: {'name': 'Alice', 'age': 30}
在这个示例中:
a
和 b
是位置参数,分别接收 1
和 2
。*args
接收了剩余的额外位置参数 3
和 4
,并将它们存储在元组中。**kwargs
接收了关键字参数 name
和 age
,并将它们存储在字典中。2.3.4. 修改已有字典的键值对
**
还可以用来更新字典,合并多个字典,或者修改一个字典的键值对。当你有一个字典并且希望将另一个字典的内容添加到该字典时,**
可以帮助解包并将字典的内容合并。
语法:
dict.update(**dict2)
示例:
person = {"name": "Alice", "age": 30}
new_info = {"age": 35, "city": "New York"}
person.update(**new_info) # 使用**new_info更新person字典
print(person)
输出:
{'name': 'Alice', 'age': 35, 'city': 'New York'}
在这个示例中,**new_info
解包了 new_info
字典中的键值对,并将其作为关键字参数传递给 update()
方法。update()
会将 new_info
中的键值对更新到 person
字典中。如果 person
中已经有某个键(例如 age
),它的值会被 new_info
中的值覆盖。
总结
**kwargs
用于在函数定义中接收不定数量的关键字参数,并将它们作为一个字典存储。**
用于在函数调用时解包字典,并将字典的键值对作为关键字参数传递给函数。**
可以与 *
结合使用,在同一个函数中同时处理位置参数和关键字参数。**
在字典操作中也非常有用,可以用来更新或合并字典内容。通过这些示例和语法,可以更好地理解 **
的多种用途,尤其是在处理不定数量的关键字参数以及字典操作时的灵活应用。
3. Python中*
与**
3.1. Python中*
与**
的区别和联系
在 Python 中,*
和 **
都是用于处理不定数量的参数,但它们的用途、语法和工作方式有所不同。以下是它们的区别和联系:
1. 区别
-
用途:
*
用于接收不定数量的位置参数,将这些位置参数打包成一个元组。它也可以在函数调用时解包可迭代对象(如列表、元组)并将其元素作为位置参数传递给函数。**
用于接收不定数量的关键字参数,将这些关键字参数打包成一个字典。它也可以在函数调用时解包字典,将字典中的键值对作为关键字参数传递给函数。-
语法:
*args
用于函数定义时接收多个位置参数,*iterable
用于解包可迭代对象并作为位置参数传递。**kwargs
用于函数定义时接收多个关键字参数,**dict
用于解包字典并作为关键字参数传递。-
类型:
*
捕获的是位置参数,通常存储为元组。**
捕获的是关键字参数,通常存储为字典。
2. 联系
-
都用于处理不定数量的参数:
*
用于处理不定数量的位置参数,**
用于处理不定数量的关键字参数。它们都使函数的参数变得更加灵活,能够接受不同数量和类型的输入。-
都可以在函数调用时使用:
- 在函数调用中,
*
和**
都能解包一个可迭代对象(如列表、元组)或字典,并将它们传递给函数。例如,*args
可以解包一个元组并将其作为位置参数传递,而**kwargs
可以解包一个字典并将其作为关键字参数传递。 -
都可以与其他参数一起使用:
*
和**
可以与位置参数和其他关键字参数一起使用,使函数调用更灵活。例如,*
可以用来接收位置参数,**
用来接收关键字参数,二者可以结合使用,处理位置参数和关键字参数。
示例对比:
1. 函数定义:
def func(a, b, *args, **kwargs):
print(a, b) # 固定位置参数
print(args) # 可变位置参数
print(kwargs) # 可变关键字参数
2. 函数调用:
func(1, 2, 3, 4, 5, name="Alice", age=30)
输出:
1 2 # 固定位置参数
(3, 4, 5) # 可变位置参数 args
{'name': 'Alice', 'age': 30} # 可变关键字参数 kwargs
总结
*
用于接收并处理不定数量的位置参数(打包为元组)和解包可迭代对象(传递给函数位置参数)。**
用于接收并处理不定数量的关键字参数(打包为字典)和解包字典(传递给函数关键字参数)。3.2. Python中*
与**
的综合使用案例
背景:
假设你正在开发一个系统,用于处理多个用户的订单。每个订单包含多个商品,商品的数量和价格可能不同。在某些情况下,订单可能包含折扣或优惠信息。为了更高效地处理订单,我们希望能够灵活地接收和处理不同数量的商品以及额外的订单信息(如折扣)。
为了实现这一目标,我们将使用 Python 的 *
和 **
来设计一个灵活的函数,使其能够接收不定数量的位置参数(商品)和不定数量的关键字参数(额外的订单信息)。
为什么使用 *
和 **
:
- 灵活性:
*
可以让我们在函数中接收任意数量的位置参数,这对于处理每个订单中数量不等的商品非常有用。 - 可扩展性:
**
可以让我们接收和处理额外的关键字参数(如折扣、优惠等),而无需修改函数的签名。
如何使用 *
和 **
:
*args
用于接收不定数量的位置参数,每个位置参数表示一个商品,商品信息以元组的形式传递。**kwargs
用于接收不定数量的关键字参数,可能包含如订单折扣、税率等额外的订单信息。示例代码:
def process_order(customer_name, *products, **order_info):
"""
处理订单,接收一个客户名称、多个商品以及额外的订单信息(如折扣、税率等)。
:param customer_name: 客户的名称
:param products: 商品的价格(不定数量)
:param order_info: 订单的额外信息(如折扣、税率等)
"""
# 计算商品总价
total_price = sum(products)
# 处理额外的订单信息
discount = order_info.get('discount', 0) # 默认没有折扣
tax_rate = order_info.get('tax_rate', 0) # 默认没有税率
# 应用折扣
total_price_after_discount = total_price - total_price * discount
# 计算税费
total_price_after_tax = total_price_after_discount * (1 + tax_rate)
# 输出订单详情
print(f"订单客户: {customer_name}")
print(f"商品数量: {len(products)}")
print(f"商品总价: ${total_price:.2f}")
print(f"折扣: {discount * 100}%")
print(f"税率: {tax_rate * 100}%")
print(f"折扣后的价格: ${total_price_after_discount:.2f}")
print(f"税后总价: ${total_price_after_tax:.2f}")
# 示例调用
process_order(
"Alice", # 客户名称
30.0, # 商品1的价格
20.0, # 商品2的价格
15.0, # 商品3的价格
discount=0.1, # 10% 折扣
tax_rate=0.08 # 8% 税率
)
示例解释:
-
函数定义:
customer_name
是一个固定位置参数,用于接收客户的名字。*products
是可变的位置参数,它接收多个商品的价格作为位置参数,并将它们打包成一个元组。**order_info
是可变的关键字参数,它接收其他与订单相关的信息(如折扣、税率等),并将它们打包成一个字典。-
函数体:
- 我们首先计算商品的总价
total_price
。 - 接着从
order_info
字典中获取折扣和税率,使用.get()
方法来处理可能不存在的键(如果没有提供折扣或税率,默认值为0
)。 - 我们计算折扣后的价格
total_price_after_discount
,然后根据税率计算税后价格total_price_after_tax
。 - 最后,我们打印出订单的相关信息,包括商品数量、总价、折扣、税率以及最终的税后总价。
-
函数调用:
- 我们传递了一个固定参数
customer_name
(客户名称),多个商品价格作为*products
参数,并通过**order_info
传递了订单的折扣和税率信息。
输出:
订单客户: Alice
商品数量: 3
商品总价: $65.00
折扣: 10.0%
税率: 8.0%
折扣后的价格: $58.50
税后总价: $63.18
总结:
*
:我们使用 *products
来接收不定数量的商品,允许用户传递任意数量的商品价格(即位置参数)。**
:我们使用 **order_info
来接收额外的订单信息(如折扣、税率等),允许用户传递灵活的订单参数(即关键字参数)。通过这种方式,我们能够高效地处理复杂的订单信息,且函数接口保持简洁和易于扩展。
作者:小南AI学院