在Python的代码中经常会见到这两个词 args 和 kwargs,前面通常还会加上一个或者两个星号。
注:这其实就是 Python 中可变参数的两种形式,并且 *args 必须放在 **kwargs 的前面,因为位置参数在关键字参数的前面。
我们将通过以下5方面来理解:
通过一个函数调用来理解“*”的作用
#定义一个含三位置参数的函数
>>>def fun(a, b, c):
... print a,b,c#传三个位置参数,调用此函数>>> fun(1,2,3)
1 2 3 # 输出
现在我们定义一个含三个整数的数列,并使用‘*’
>>> var = [1, 2, 3]
>>> fun(*var)
1 2 3 # 输出
'*'做了什么?它拆开数列'var'的数值作为位置参数,并把这些位置参数传给函数‘fun’来调用。
这意味着fun(*var)与fun(1,2,3)是等效的,因为var=[1,2,3]
注:*var与位置参数可混合使用
"*args"在函数定义中是做什么用的?它接收元组作为位置参数,而非是常见的参数列表。在这里,“args”是个元组。
“args”与“常规参数列表”混合使用
#在函数定义中,参数‘a’代表‘常规参数列表’
>>> def fun(a, *args):
... print("a is ", a)
... print("args is ", args)
#‘args’接收除常规参数之外的位置参数作为元组>>> fun(11, 12, 13, 14)
a is 11
args is (12, 13, 14)
注:'args'既然是元组,我们就可以遍历它。
通过一个函数调用来理解“**”的作用
# 定义一个三参数的函数
>>> def fun(a, b, c):
... print(a, b, c)...>>> func(1, 2, 3)
1 2 3
>>> fun(a=1, b=2, c=3)
1 2 3
使用“**”调用函数,这种方式我们需要一个字典。
>>> var = {'b' : 2, 'c' : 3}
>>> fun(1, **var)
1 2 3
注:在函数调用中使用‘*’,我们需要元组;在函数调用中使用‘**’,我们需要一个字典
“**”做了什么?它unpack字典,并将字典中的数据项作为键值参数传给函数。因此,fun(1, **var)的写法与fun(1, b=2, c=3)等效。
在函数定义中“**kwargs”意味着什么?kwargs接收除常规参数列表外的键值参数字典,在这里'kwargs'是个字典。
重新定义函数:
>>> def fun(a, **kwargs):
... print("a is ", a)
... print("We expect kwargs 'b' and 'c' in this function")
... print("b is ", kwargs['b'])
... print("c is ", kwargs['c'])
...>>> fun(1, b=3, c=5)
a is 1
We expect kwargs 'b' and 'c' in this function
b is 3
c is 5
注:在一个字典前使用“**”可以unpack字典,传字典中的数据项作为键值参数。
通过一个应用实例来说明'args','kwargs'应用场景以及为何要使用它。
在继承类和重写方法的时候,我们可以用到’*args’和’**kwargs’将接收到的位置参数和键值参数给父类方法,通过实例我们更好的理解。
>>> class Model(object):
... def __init__(self, name):
... self.name = name... def save(self, force_update=False, force_insert=False):
... if force_update and force_insert:
... raise ValueError("Cannot perform both operations")
... if force_update:
... print("Updated an existing record")
... if force_insert:
... print("Created a new record")
构造一个新类,类有’Model’的行为,但我们只有检查一些条件后才会保存这个类的对象。这个新类继承’Model’,重写’Model’的’save()’
>>> class ChildModel(Model):
... def save(self, *args, **kwargs):
... if self.name=='abcd':
... super(ChildModel, self).save(*args, **kwargs)... else:
... return None
实际上对应的保存动作发生在’Model’的’save’方法中。所以我们调用子类的的’save()’方法而非’Model’的方法.子类ChildModel的’save()’接收任何父类save()需要的参数,并传给父类方法。因此,子类’save()’方法参数列表中有”*args”和”**kwargs”,它们可以接收任意位置参数或键值参数,常规参数列表除外。
下面创建ChildModel实体并保存:
>>> c=ChildModel('abcd')
>>> c.save(force_insert=True)
Created a new record
>>> c.save(force_update=True)
Updated an existing record
这里传键值参数给对象的save()方法,调用的是子类的save()。