美文网首页Python
Python:*args 和 **kargs 的作用、用法和区别

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

作者: dex0423 | 来源:发表于2020-08-20 09:45 被阅读0次

    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

    • python 中规定参数前带 * 的称为可变位置参数,只是我们通常称这个可变位置参数为 *args,我们也可以写成 *any 等形式。
    • *args 是一个 元组,传入的参数会被放进元组里。

    场景一:

    • *args 将多个参数存储在 元组 类型的变量 args 当中,当我们在函数内部使用 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'         # 注意这里打印的 并不是元组
    >>>
    
    • 上面代码中,arg_1arg_2 是固定参数,传参的时候函数会将第一、二个参数按位置赋给 arg_1arg_2,就是代码中的 ab
    • 而后面 cde,则一并存储为 元祖 传给 args,所以才会在打印的结果中看到打印的返回值:args : ('c', 'd', 'e')

    场景二:

    • 用于容器传参,即函数被调用时传入了一个容器(如 list),容器中包含多个参数,而这些参数分别对应函数的参数。
    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
    
    • 在上面的例子中,func(1, *arg_list) 其实就等效于 func(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

    • python 中规定参数前 带 ** 的,称为可变关键字参数,通常用 **kwargs 表示。
    • ** kwargs:是一个字典,传入的参数以 键值对 的形式存放到字典里,相比较而言,*args 是一个 元组,到这里读者应该明白二者的区别了吧。

    场景一:

    • **kargs 实质就是将函数的参数和值,存储在 字典类型 的 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
    

    场景二:

    • **kargs的第二种使用场景,与 *args 第二种使用场景类似,也是用于传递参数到函数中,只不过区别是 *args 可以传入的是列表、元组等,而 **kargs 传入的是字典。
    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)
    第一个参数 第二个参数的值 第三个参数的值
    
    • 至于参数的数量,情况和 *args 一样,传入字典的元素数量和函数所需的参数数量需要保持一致,否则会报错。
      下面是一个例子:
    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': '第二个字典参数'}
    第一个字典参数
    第二个字典参数
    

    相关文章

      网友评论

        本文标题:Python:*args 和 **kargs 的作用、用法和区别

        本文链接:https://www.haomeiwen.com/subject/szhdjktx.html