Usage of *args and **kwargs in Python

When I first started learning python, I was confused about the use of args, kwargs, and * . I believe there are many people who are confused about this. I intend to resolve this doubt (hopefully less) through this thread.

Let's understand it through the following 5 steps: 
1. Understand the effect of '*' through a function call 
2. Understand the meaning of '*args'  through a function definition
3. Understand '**' through a function call
4. Understand the meaning of '**kwargs' through the definition of a function  5. 
Explain the application scenarios of 'args', 'kwargs' and why to use it through an application example


Understanding what '*' does with a function call

Define a function "fun" with three positional arguments.

>>> def fun(a,b,c): ... print a,b,c ... 

Call this function with three positional arguments

>>> fun(1,2,3)
1 2 3 #输出

You can see that calling this function with three positional parameters will print out three parameters.

Now we define a sequence of three integers and use '*'

>>> l = [1,2,3]
>>> fun(*l) 1 2 3 #输出

'*' What have you done?

It unpacks the values ​​of the sequence 'l' as positional parameters, and passes these positional parameters to the function 'fun' to call.

Therefore, splitting the columns and passing positional parameters means that fun(*l) and fun(1,2,3) are equivalent, because l = [1,2,3].

Try using other values ​​in the sequence

>>> l = [4,8,0]
>>> fun(*l) 4 8 0 #输出

Next, we try to put four values ​​in the sequence. What happens when we call the function?

>>> l = [3,6,9,1] >>> fun(*l) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: fun() takes exactly 3 arguments (4 given)

In this call we did not get the proper result, the TypeWrror exception was triggered. It's easy to see the error "fun() takes exactly 3 arguments (4 given)".

Why does this happen?

The sequence 'l' contains four values. Therefore, we try to call 'fun(*l)', and the values ​​in 'l' are split and passed to the function fun as positional parameters. However, there are four values ​​in 'l', calling 'fun(*l)' is equivalent to calling 'fun(3,6,9,1)', and because only three positional parameters are used in the definition of the function 'fun', Hence we get this error. In the same way, in the same steps, there are two numerical values ​​in the sequence 'l', pay attention to the error content.

>>> l = [7,4]
>>> fun(*l)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module> TypeError: fun() takes exactly 3 arguments (2 given)

'*l' mixed with positional parameters

>>> fun(23, *l)
23 7 4

Here, we give a positional parameter 23, and two values ​​7 and 4 removed from the sequence 'l', so the three parameters 23, 7 and 4 are passed to the function 'fun'

Understanding the meaning of '*args' through the definition of a function

Modify the definition of the function:

>>> def fun(*args): ... print args ... 

Call this function with a positional argument

>>> fun(13)
(13,)

Call this function with multiple parameters

>>> fun(11,93,43)
(11, 93, 43)

What is '*args' used for in function definitions?

It accepts tuples as positional arguments instead of the usual argument list. Here, "args" is a tuple. Don't worry about the understanding of the "common parameters" part of our explanation, which will gradually become clear in the following examples. In the previous example, when the function was called to print "args", it would print all the values ​​contained in the tuple.

We redefine the function, using "*args" mixed with "regular argument list"

>>> def fun(a, *args): ... print "a is ", a ... print "args is ", args ... 

In this function definition, the parameter "a" stands for "regular parameter list". 
Call this function with four positional arguments:

>>> fun(11,12,34,43) a is 11 args is (12, 34, 43)

It's easy to see that 'a' prints out 11, the first positional argument. There is only one argument '*args' after 'a'. Therefore, 'args' receives positional arguments other than regular arguments as a tuple. So the tuple args receives 12, 34 and 43 as a tuple.

We can also call this function passing a positional argument:

>>> fun(91)
a is  91
args is ()

Here, the only argument we pass is assigned to the regular argument 'a'. Therefore, 'args' receives an empty tuple.

Now that we have "args", we can extract the required values ​​to do what we want. Redefine "fun":

>>> def fun(a, *args): ... print a ... print "args can receive a tuple of any number of arguments, let's print all that." ... for arg in args: ... print arg ... 

Now we call this function with any arguments:

>>> fun(1,5,6,7) 1 args can receive a tuple of any number of arguments, let's print all that. 5 6 7

Since 'args' is a tuple, we can iterate over it.

Now we consider the scenario using all available parameters. We need to use two functions, the first function takes any number of parameters, and the other function calculates the sum of the other parameters except the first parameter. Weird use case, but let's just review what we've done so far. Our purpose is to take variable parameters in one function and pass those parameters to another function.

In the first step we write a function to compute the sum. In this use case, this function will be applied in the first function.

>>> def calculate_sum(*args): ... return sum(args) ... 

In this function, we use the built-in function 'sum', which takes a tuple or array as an argument and returns the sum of all elements of the tuple. As can be seen from the definition of the function 'args' accepts a tuple containing the positional arguments passed to this function. Therefore, 'args' is a tuple, briefly as an argument to the function 'sum'. Next, define another function that takes any number of parameters and uses the previous function to calculate the sum of the other parameters except the first parameter.

>>> def ignore_first_calculate_sum(a,*iargs): ... required_sum = calculate_sum(*iargs) ... print "sum is ", required_sum ... 

We can pass any number of parameters to this function. The first argument is received by the regular argument 'a', the other arguments are received as a tuple by 'iags'. As in the case we considered, compute the sum of the other parameters except the first one. Therefore, we receive the first argument with 'a', and 'iags' is a tuple containing the other arguments. We use the function 'calculate_sum', but 'calculate_sum' requires multiple positional arguments passed as tuples to 'args'. So in the function 'ignore_first_calculate_sum' you need to split the tuple 'iags', and then pass the element as a positional parameter to 'calculate_sum'. Note, use '*' to split the tuple.

So, we call 'required_sum=calculate_sum(*iags)' like this.

'required_sum=calculate_sum(iags)' cannot be called like this because we need to unpack the value before passing to 'calculate_sum'. Without '*' the value will not be unpacked and the desired action will not be performed. The calling function is as follows:

>>> ignore_first_calculate_sum(12, 1,4,5) sum is 10 >>> ignore_first_calculate_sum() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: ignore_first_calculate_sum() takes at least 1 argument (0 given)

get the desired result.

Understand the role of '**' through a function call

Define a function with three parameters and call it in multiple ways:

>>> def fun(a, b, c): ... print a, b, c ... >>> fun(1,5,7) 1 5 7 >>> fun(a=1,b=5,c=7) 1 5 7

Use "**" to call a function, this way we need a dictionary. Note: use "*" in a function call, we need a tuple; use "**" in a function call, we need a dictionary

>>> d={'b':5, 'c':7} >>> fun(1, **d) 1 5 7

What does "**" do in a function call?

It unpacks the dictionary and passes the data items in the dictionary to the function as key-value arguments. Therefore, "fun(1, **d)" is equivalent to "fun(1, b=5, c=7)". 
For better understanding, here are a few more examples:

>>> d = {'c':3}
>>> fun(1, 4, **d) 1 4 3 >>> d = {'a':7, 'b':3, 'c':8} >>> fun(**d) 7 3 8

Let's make some mistakes:

>>> d = {'a':7, 'b':3, 'c':8, 'd':90} >>> fun(**d) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: fun() got an unexpected keyword argument 'd'

This call is equivalent to 'fun(a=7, b=3, c=8, d=90)', but the function takes only three arguments, so we get a TypeError

>>> d = {'a':7, 'b':3,'d':90} >>> fun(**d) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: fun() got an unexpected keyword argument 'd'

fun(**d) is equivalent to fun(a=7, b=3, d=90). The number of parameters passed to the function "fun", but there is no 'd' in the parameter list, and 'd' in the call 'Key-value parameter passed to function causes TypeError.

So, “*” unpacks the dictionary i.e the key values pairs in the dictionary as keyword arguments and these are sent as keyword arguments to the function being called. “” unpacks a list/tuple i.e the values in the list as positional arguments and these are sent as positional arguments to the function being called.

Understanding the meaning of '**kwargs' through function definitions

Redefine function "fun":

>>> def fun(a, **kwargs): ... print a, kwargs ... 

This function only takes one positional parameter, because there is only one variable 'a' in the regular parameter list. But through "**kwargs", multiple key-value parameters can be passed.

>>> fun(1, b=4, c=5)
1 {'c': 5, 'b': 4} >>> fun(45, b=6, c=7, d=8) 45 {'c': 7, 'b': 6, 'd': 8}

What does "**kwargs" mean in a function definition? 
Functions are defined with "**kwargs", where kwargs receives a dictionary of key-valued arguments in addition to the regular argument list positions. Here 'kwargs' is a dictionary.

Redefine the function:

>>> 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

Error calling:

>>> fun(1, b=3, d=5)
a is 1 We expect kwargs 'b' and 'c' in this function b is 3 c is Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in fun KeyError: 'c'

In the above call, both the positional parameter 'a' and the key-value parameter 'b' are printed. The other key-value parameter passed in is 'd', the function expects a key-value parameter 'c', and gets it from the dictionary 'kwargs'. But if the key value 'c' is not passed in, a KeyError is raised. If the key value 'c' is passed in, this error will not be raised

>>> fun(1, b=3, d=5, c=9) a is 1 We expect kwargs 'b' and 'c' in this function b is 3 c is 9

Since '**kwargs' is in the function parameter list, we can pass any number of key-value parameters. The above call passes in "d", but the function doesn't use it.

Another error:

>>> fun(1, {'b':2, 'c':34}) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: fun() takes exactly 1 argument (2 given)

As the error suggests, the function 'fun' only needs one positional argument, but was given two. Although 'kwargs' accepts key-value arguments as a dictionary, you cannot pass a dictionary as positional arguments to 'kwargs'. You can call it like this:

>>> fun(1, **{'b':2, 'c':34}) a is 1 We expect kwargs 'b' and 'c' in this function b is 2 c is 34

Use "**" before a dictionary to unpack the dictionary and pass the data items in the dictionary as key-value parameters.

Explain the application scenarios of 'args', 'kwargs' and why to use it through an application example

Whenever inheriting a class and overriding a method, we should use '*args' and '**kwargs' to pass the received positional and key-value parameters to the parent method. We understand better through examples

>>> 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" ... 

Defining a class, we can create an object of the class, and the object of the class has a method 'save()'. Assume that the object of the class can be saved to the database through the save() method. Use the function save() parameter to decide whether to create a record in the database or update an existing record. 
Construct a new class, the class has the behavior of 'Model', but we only save the object of this class after checking some conditions. This new class inherits 'Model' and overrides 'save()' of 'Model'

>>> class ChildModel(Model): ... def save(self, *args, **kwargs): ... if self.name=='abcd': ... super(ChildModel, self).save(*args, **kwargs) ... else: ... return None ... 

Actually the corresponding save action happens in the 'save' method of 'Model'. So we call the 'save()' method of the subclass instead of the 'Model' method. The 'save()' of the subclass ChildModel accepts any parameters required by the parent class save() and passes it to the parent class method. Therefore, the subclass 'save()' method has "*args" and "**kwargs" in the parameter list, which can receive arbitrary positional or key-value parameters, except for the regular parameter list.

Create the ChildModel entity below and save it:

>>> c=ChildModel('abcd')
>>> c.save(force_insert=True)
Created a new record
>>> c.save(force_update=True) Updated an existing record

Here the part-time parameter is passed to the save() method of the object. The subclass's save() was called, and 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.

Reprinted from: https://blog.csdn.net/callinglove/article/details/45483097

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325387385&siteId=291194637