Python

Python:*args 和 **kargs 的作用、用法和区别

2020-08-20  本文已影响0人  dex0423

1. *args 和 **kargs 到底有什么用

在 python 中,*args**kwargs 都用作向函数传递 可变参数
所谓 可变,其实就是参数的 数量位置形式 是不固定的。
之所以使用可变参数,目的是为了在构造函数的时候,增加函数功能的灵活性,同时函数写法会更加优雅。

在编程工作中,经常需要传递很多参数到函数中,但这些参数 有的时候需要用到、有时候则不需要用到,甚至有时候参数根本不存在,最典型的例子就是 构造装饰器

注意:如果看不懂可以跳过下面这一部分!

下面是一个 timer 装饰器,用来计算并打印函数执行花费的时间,在构造参数的时候用到了 *args**kwargs 两个参数。

def timer(func):
...     def call_func(*args, **kwargs):
...         print(f"计时开始 [{time.strftime('%Y-%m-%d %X', time.localtime())}]")
...         start_time = time.time()
...         try:
...             func(*args, **kwargs)
...         except KeyboardInterrupt:
...             pass
...         finally:
...             end_time = time.time()
...             total_time = end_time - start_time
...             print("计时结束")
...             print(f"程序用时{int(total_time // 60)}分{total_time % 60:.2f}秒")
...     return call_func
... 
@timer
... def test(arg_1, arg_2, arg_3):
...     print("开始 执行 test 函数")
...     time.sleep(3)
...     print(f"打印参数 arg_1: {arg_1} arg_1: {arg_1} arg_1: {arg_1}")
...     print("执行 test 函数 结束")
...     
test(1, 2, 3)
计时开始 [2020-10-30 17:50:23]
开始 执行 test 函数
打印参数 arg_1: 1 arg_1: 1 arg_1: 1
执行 test 函数 结束
计时结束
程序用时0分3.01秒

上面的装饰器之所以传递 可变参数,是因为装饰器需要装饰的函数的数量是不固定的,如果传递固定的参数,那么被装饰的函数在传参的时候会报错。

2. *args 和 **kargs 用法有什么区别

2.1. 可变位置参数 *args

场景一:

注意:如果在函数内部使用 *args 获取参数, *args 得到的结果 并不是元组,而是分开的多个参数值,这与 args 获取的结果是不一样的。

>>> def func_args(arg_1, arg_2, *args):
...     print(f"arg_1 : {arg_1}")
...     print(f"arg_2 : {arg_2}")
...     print(f"args : {args}")
...     print(f"*args : {*args}")    # 注意这里获取的是 *args
...
>>> func_args('a', 'b', 'c', 'd', 'e')
arg_1 : a
arg_2 : b
args : ('c', 'd', 'e')
*args : 'c', 'd', 'e'         # 注意这里打印的 并不是元组
>>>

场景二:

def func(arg1, arg2, arg3):
...     print(arg1, arg2, arg3)
... 
... arg_list=["second", 3]
... func(1, *arg_list)       # 此处用 *arg_list 这个 list 传入 "second" 和 3 两个参数
1 second 3

注意:用的时候注意,调用时 传入的参数总量(即容器外参数和容器内元素个数的总和),必须和 构造函数所需的参数数量 保持一致。以上面的例子为例,构造函数时 arg1, arg2, arg3 共计 三个 参数,调用函数时传入 1, "second", 3 加起来也是 三个。 如果数量不一样,传入的太多或者太少都会报错。

下面是一个例子:


... arg_list=["second", 3, 4, 5]        # 传递的参数 太多
... func(1, *arg_list)
Traceback (most recent call last):
  File "<input>", line 5, in <module>
TypeError: func() takes 3 positional arguments but 5 were given  
... 
... arg_list=["second",]                # 传递的参数 太少
... func(1, *arg_list)
Traceback (most recent call last):
  File "<input>", line 6, in <module>
TypeError: func() missing 1 required positional argument: 'arg3'

2.2. 可变关键字参数 **kargs

场景一:

def func_kwargs(arg_1, **kwargs):
...     print(f"第一个参数 arg_1 : {arg_1}")
...     print(f"后面的参数 kwargs : {kwargs}")
...     
func_kwargs('a', arg_2='b', arg_3='c', arg_4='d')    # 注意参数格式一定写成 key=value 形式
第一个参数 arg_1 : a
后面的参数 kwargs : {'arg_2': 'b', 'arg_3': 'c', 'arg_4': 'd'}

在上面的例子中,arg_2='b', arg_3='c', arg_4='d'的含义是:arg_2、arg_3、arg_4 三个参数的值分别为 'b'、'c'、'd',在参数传入函数后,他们一起被构造成一个字典,即打印出的 kwargs : {'arg_2': 'b', 'arg_3': 'c', 'arg_4': 'd'}

注意:传入的可变参数一定要写成 key=value 的形式,否则会报 赋参太多 错误。

详见下面的赋参太多错误的示例:

def func_kwargs(arg_1, **kwargs):
...     print(f"第一个参数 arg_1 : {arg_1}")
...     print(f"后面的参数 kwargs : {kwargs}")
... 
func_kwargs('a', 'b', 'c', 'd')          # 直接传递参数
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: func_kwargs() takes 1 positional argument but 4 were given

场景二:

def func(arg_1, arg_2=None, arg_3=None):
...     print(arg_1, arg_2, arg_3)
... 
arg_dic={"arg_2": "第二个参数的值", "arg_3": "第三个参数的值"}    # 需要传入的字典
func("第一个参数", **arg_dic)
第一个参数 第二个参数的值 第三个参数的值

在上面的例子中,arg_dic={"arg_2": "第二个参数的值", "arg_3": "第三个参数的值"} 以字典形式传入函数 func,在传入后自动匹配赋值: arg_2="第二个参数的值", arg_3="第三个参数的值"

注意:在构造函数的时候,参数 不一定 要写成 key=value 的形式,按照普通函数的形式来写,也能正常接收传入的字典形式的参数。

下面是普通的函数构造写法的例子:

def func_1(arg_1, arg_2, arg_3):    # 普通的函数参数写法
...     print(arg_1, arg_2, arg_3)
...     
arg_dic={"arg_2": "第二个参数的值", "arg_3": "第三个参数的值"}
func_1("第一个参数", **arg_dic)
第一个参数 第二个参数的值 第三个参数的值
def func(arg_1, arg_2=None, arg_3=None):
...     print(arg_1, arg_2, arg_3)
...     
arg_dic={"arg_2": "第二个参数的值", "arg_3": "第三个参数的值", "arg_4": "第四个参数的值"}
func("第一个参数", **arg_dic)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: func() got an unexpected keyword argument 'arg_4'

3. *args 和 **kargs 一起使用

def func(arg_1, arg_2="第二个参数的值", *args, arg_3="第三个参数的值", arg_4, **kwargs):
...     print(arg_1)     # [0, 1, 2]
...     print(arg_2)     # 3          # 注意:这里的 3,对应构造函数的 arg_2,它是参数 *[3, 4, 5] 传进来的!!!
...     print(args)      # (4, 5)
...     print(*args)     # 4 5        # 注意:这里返回的并不是元组
...     print(arg_3)     # 6
...     print(arg_4)     # 7
...     print(kwargs)    # {'key_1': '第一个字典参数', 'key_2': '第二个字典参数'}
...     # print(**kargs)
...     print(kwargs["key_1"])    # 第一个字典参数
...     print(kwargs["key_2"])    # 第二个字典参数

func([0, 1, 2], *[3, 4, 5], arg_3=6, arg_4=7, key_1="第一个字典参数", key_2="第二个字典参数")

[0, 1, 2]
3             
(4, 5)
4 5
6
7
{'key_1': '第一个字典参数', 'key_2': '第二个字典参数'}
第一个字典参数
第二个字典参数
上一篇下一篇

猜你喜欢

热点阅读