深入理解‘*’,‘*args’,‘**kwargs’之间的区别

有关* 、*args、**kwargs通过以下五步来理解:

  1. 通过一个函数理解‘*’的作用
  2. 通过一个函数的定义来理解‘*args’的含义
  3. 通过一个函数的调用来理解‘**’的作用
  4. 通过一个函数的定义来理解‘**kwargs’的含义
  5. 通过一个应用实例来说明‘args’,‘kwargs’应用场景以及为何要使用

通过一个函数调用来理解‘*’的作用:

定义一个含三个位置参数的函数‘fun’:

传入三个位置参数调用此函数:

可以看出三个位置参数调用此函数,会打印出三个参数

现在我们定义一个含三个整数的数列,并使用‘*’

*’做了什么?

它拆开了数列‘l’的数值作为位置参数,并把这些位置参数传给函数‘fun’来调用。因此,拆数列、传位置参数意味着fun(*l)与fun(1,2,3)是等效的,因为 l = [1,2,3]。

试着数列中使用其他数值

接下来,我们看看数列中放四个数值,调用函数会出现什么情况呢?

在这次的调用中我们并没有得到合适的结果,触发了TypeError异常。很容易看到错误内容“fun() takes exactly 3 arguments (4 given)”。

为什么会发生这种情况呢?

数列‘l’含有四个数值。因此,我们试图调用‘fun(*l)’,’l’中数值拆开传给函数fun作为位置函数。但是‘l’中有四个数值,调用‘fun(*l)’相当于调用‘fun(3,6,9,1)’,又因为函数‘fun’定义中只用三个位置参数,因此我们得到这个错误。同理,同样的步骤,数列‘l’中两个数值情况,注意 error内容。

‘*l’与位置参数混合使用

在这里给出一个位置参数23, 和从数列‘l’拆除的两个数值7和4,因此三个参数23,7,4传给了函数‘fun’

通过一个函数定义来理解‘*args’的含义

修改函数定义:

传一个位置参数调用此函数

传入多个参数调用此函数

*args’在函数定义中是做什么用的?

它接收元组作为位置参数,而非是常见了参数列表。在这里,‘args’是一个元组。在我们的解释中不要担心‘常见的参数’这部分的理解,这个会在接下来的例子中逐渐明了。在上个例子中,调用函数打印‘args’时,他会打印元组中包含的所有值。

我们重新定义函数,‘*args’与‘常规参数列表’混合使用

在上述函数定义中,参数‘a’代表‘常规参数列表’。

传四个位置参数调用此函数:

很容易看到,‘a’打印出为11,即第一个位置参数。‘a’之后只有一个参数‘*args’。因此,‘args’接收除了常规参数之外的位置参数作为元组。因此,元组args作为元组接收12,34,43。

我们也可以传入一个位置参数来调用此函数:

在这里,我们传入的唯一一个参数分配给了常规参数‘a’。因此,‘args’接收一个空元组。 既然我们获取了‘args’,我们可以提取需要的数值来做我们想做的事情。重新定义‘fun’:

现在我们传任意个参数来调用此函数:

‘args’既然是元组,我们就可以遍历它。

现在我们考虑使用所有能得到的参数的场景。我们需要使用两个函数,第一个函数带有任意个参数,并通过另外一个函数计算除第一个参数的其他参数之和。我们的目的就是在一个函数中获取可变参数,并把这些参数给另外一个函数。

第一步,我们写一个函数计算和。在这个用例中,这个函数会在第一个函数中应用。

在这个函数中,我们使用内建函数‘sum’,它使用元组或数列作为参数,返回元组所有元素的和。从函数的定义可以看出‘args’接收包含传给此函数位置参数的元组。因此,‘args’是一个元组,作为函数‘sum’的参数。接下来定义另外一个函数,该函数有任意个参数,并利用上一个函数来计算除了第一个参数之外的其他参数的和。

我们可以传任意个参数给这个函数。第一个参数被常规参数‘a’接收,其他参数被‘iargs’作为元组接收。正如我们考虑的案例,计算除第一个参数之外的其他参数的和。因此,我们用‘a’接收第一个参数,‘iargs’是包含其他参数的元组。我们用到函数‘calculate_sum’,但‘calculate_sum’需要多个位置参数作为元组传给‘args’。所以在函数‘ignore_first_calculate_sum’需要拆元组‘iargs’,将元素作为位置参数传给‘calculate_sum’,注意:用‘*’拆元组。

所以我们这样调用’required_sum=calculate_sum(*iargs)’.

‘required_sum=calculate_sum(iargs)’不能这么调用,因为传给’calculate_sum’之前我们需要unpack数值。不使用’*’将不会unpack数值,也就不能执行想要的动作。调用函数如下:

通过一个函数的调用来理解‘**’的作用

定义一个三个参数的函数,并用多种方式调用:

使用‘**’调用函数,这种方式我们需要一个字典,注意:在函数调用中使用‘*’,我们需要元组;在函数调用中使用‘**’,我们需要一个字典。

在函数调用中‘**’做了什么?

unpack字典,并将字典的数据项作为键值参数传给函数。因此,‘fun(1,**d)’的写法与‘fun(1,b=5,c=7)’等效。

为了更好的理解再多举几个例子:

让我们制造一些错误:

这次调用等同于‘fun(a=7,b=3,c=8,d=90)’,但是函数只需要三个参数,因此我们得到TypeError

通过函数定义来理解‘**kwargs’的含义

重新定义函数‘fun’:

此函数只有一个位置参数,因为常规参数列表只有一个变量‘a’,但是通过‘**kwargs’可以传入多个键值参数。

在函数定义中,‘**kwargs’意味着什么?

用‘**kwargs’定义函数,kwargs接收除了常规参数列表之外的键值参数字典。在这里,‘kwargs’是一个字典;

重新定义函数:

错误调用:

上面的调用,位置参数‘a’键值参数‘b’都打印出来了。传入的其他键值参数是‘d’,函数需要的键值参数是’c’,并从字典‘kwargs’中获取。但没有传入键值‘c’,引发了KeyError。如果传入了键值‘c’就不会引发这个错误。

由于‘**kwargs’在函数参数列表中,我们可以传入任意个键值参数。上面的调用传入了‘d’,但是函数没有用到。

另外一个错误:

正如错误提示:函数‘fun’只需要一个位置参数,却给了两个。尽管‘kwargs’接收键值参数作为一个字典,但你不能传一个字典作为位置参数给‘kwargs’。但是可以这样调用:

在一个字典前使用‘**’可以unpack字典,传字典中的数据项作为键值参数。

通过一个应用实例来说明‘args’,‘kwargs’应用场景以及为何要使用它

在任何时候继承类和重写方法的,我们应当用到‘*args’和‘**kwargs’将接收到的位置参数和键值参数给父类方法。通过实例我们可以更好的理解:

定义一个类,我们可以创建类的对象,类的对象有一个方法’save()’.假设类的对象可以通过save()方法保存到数据库中。通过函数save()参数来决定是否在数据库中创建一条记录或者更新现存的记录。

构造一个新类,类有’Model’的行为,但我们只有检查一些条件后才会保存这个类的对象。这个新类继承’Model’,重写’Model’的’save()’

实际上对应的保存动作发生在’Model’的’save’方法中。所以我们调用子类的的’save()’方法而非’Model’的方法.子类ChildModel的’save()’接收任何父类save()需要的参数,并传给父类方法。因此,子类’save()’方法参数列表中有”*args”和”**kwargs”,它们可以接收任意位置参数或键值参数,常规参数列表除外。

下面创建ChildModel实体并保存:

这里传兼职参数给对象的save()方法。调用的是子类的save(),It received a dictionary containing the keyword argument in “kwargs”. Then it used “**” to unpack this dictionary as keyword arguments and then passed it to the superclass save(). So, superclass save() got a keyword argument ‘force_insert’ and acted accordingly.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值