在Python的函数定义中,*args和kwargs是两种用于处理可变数量参数的特殊语法,能够让函数接收不确定数量的输入参数,大幅提升函数的通用性。很多开发者在初学阶段会分不清两者的区别,也不清楚具体的使用场景,下面将从基础概念开始逐步讲解。
*args的基本概念与用法
*args是arguments的缩写,用于在函数定义中接收任意数量的位置参数,这些参数会被打包成一个元组(tuple)传递给函数内部。需要注意的是,args只是约定俗成的名称,前面的星号*才是关键语法,换成其他名称比如*params也是合法的,不过行业内普遍使用args作为命名。
下面通过一个简单的示例展示*args的基础用法:
def sum_numbers(*args):
# args是一个元组,包含所有传入的位置参数
total = 0
for num in args:
total += num
return total
# 传入不同数量的位置参数调用函数
print(sum_numbers(1, 2)) # 输出3
print(sum_numbers(1, 2, 3, 4)) # 输出10
print(sum_numbers(5)) # 输出5
从上面的代码可以看出,调用函数时传入的多个位置参数,都会被收集到args这个元组中,函数内部可以像操作普通元组一样处理这些参数。
*args的传参规则
使用*args时需要注意,它只能接收位置参数,不能接收关键字参数,如果传入关键字参数会直接抛出类型错误:
def test_args(*args):
print(args)
# 传入关键字参数会报错
test_args(a=1, b=2) # TypeError: test_args() got an unexpected keyword argument 'a'
**kwargs的基本概念与用法
**kwargs是keyword arguments的缩写,用于在函数定义中接收任意数量的关键字参数,这些参数会被打包成一个字典(dict)传递给函数内部。同样,kwargs是约定俗成的名称,前面的两个星号**才是核心语法,也可以换成其他名称比如**kw。
下面是**kwargs的基础用法示例:
def print_info(**kwargs):
# kwargs是一个字典,键是参数名,值是参数值
for key, value in kwargs.items():
print(f"{key}: {value}")
# 传入不同数量的关键字参数调用函数
print_info(name="张三", age=20)
# 输出:
# name: 张三
# age: 20
print_info(city="北京", job="程序员", hobby="跑步")
# 输出:
# city: 北京
# job: 程序员
# hobby: 跑步
调用函数时传入的所有关键字参数,都会被收集到kwargs字典中,函数内部可以通过字典的操作方式获取对应的参数名和参数值。
**kwargs的传参规则
**kwargs只能接收关键字参数,不能接收位置参数,传入位置参数会直接报错:
def test_kwargs(**kwargs):
print(kwargs)
# 传入位置参数会报错
test_kwargs(1, 2) # TypeError: test_kwargs() takes 0 positional arguments but 2 were given
*args和**kwargs混合使用
在实际开发中,经常会同时需要接收位置参数和关键字参数,这时候可以将*args和**kwargs结合使用。需要注意的是,函数定义时参数顺序有严格要求:普通参数 > *args > **kwargs,否则会抛出语法错误。
下面是一个混合使用的示例:
def mixed_func(a, b, *args, **kwargs):
print(f"普通参数a: {a}, b: {b}")
print(f"位置可变参数args: {args}")
print(f"关键字可变参数kwargs: {kwargs}")
# 调用函数
mixed_func(1, 2, 3, 4, 5, name="李四", score=90)
# 输出:
# 普通参数a: 1, b: 2
# 位置可变参数args: (3, 4, 5)
# 关键字可变参数kwargs: {'name': '李四', 'score': 90}
从上面的输出可以看到,1和2被赋值给普通参数a和b,剩余的3、4、5被收集到args元组中,name和score两个关键字参数被收集到kwargs字典中,符合参数的分配规则。
常见使用场景
- 封装通用工具函数:比如上面的求和函数,不需要提前定义固定数量的参数,传入任意数量的数值都能计算总和。
- 函数装饰器:装饰器通常需要接收被装饰函数的所有参数,使用*args和**kwargs可以保证不遗漏任何参数。
- 子类继承父类方法:子类重写父类方法时,使用*args和**kwargs可以接收父类方法的所有参数,避免参数遗漏导致的错误。
注意事项
使用*args和**kwargs时,不需要在调用函数的时候手动给参数加星号,星号仅在函数定义时用于打包参数,函数内部使用args和kwargs时也不需要加星号,直接使用即可。
如果需要在调用函数时把一个元组或字典拆分成参数传入,才需要在调用时使用星号:
def add(a, b, c):
return a + b + c
nums = (1, 2, 3)
# 调用时用*拆分元组作为位置参数
print(add(*nums)) # 输出6
info = {"a": 10, "b": 20, "c": 30}
# 调用时用**拆分字典作为关键字参数
print(add(**info)) # 输出60