Understanding *args and **kwargs in Python

write in front

In the process of reading code, we often see this expression containing *args and **kwargs:
For example, what should this output?

def foo(*args):
    print(args)


foo(1, 2, 3, 4, 5)

what about this?

def foo(a, *args):
    print('a:', a)
    print('args:', args)


foo(1, 2, 3, 4, 5)

And what about this?

def bar(a,b,c):
    print(a,b,c)

bar(*[1,2,3])

Huh? Why does the number ? appear at the front of a list? is this correct?

*args and **kwargs, as well as separate *, what exactly do ** do? What is the principle? After reading this article you will understand completely!

Article directory

  • *argc
    • Packing parameters
    • Split parameters
  • **kwargs
    • Packing parameters
    • Split parameters

*args consists of two parts – * and args. The focus here is *.
So in order to explain clearly *args, we have to trace back to the source – understand the role of *.
Hitting the blackboard here, here comes the key point, which is also something that many blogs have not mentioned: There are two functions of ? – packing parameters (pack) and splitting parameters (unpack)!

*argc

Packaging parameters

example 1:

def foo(*number):
    print(number)


foo(1, 2, 3, 4, 5)

(1, 2, 3, 4, 5)

What did we see? Give the function 5 parameters, it runs successfully, and the output is a tuple composed of parameters.
We know that if there is no ? sign before number, then it is obvious that foo() can only accept one parameter, and an error will be reported if there are too many parameters or too few. And by adding ?, it can run successfully.
So what is the principle?
The answer is:? Pack the multiple parameters 1,2,3,4,5 received by function foo() into a tuple (1,2,3,4,5 ), assigned to the formal parameter number.
We can verify it:
Example 2:

def foo(*number):
    for i in number:
        print(i)
    print(type(number))


foo(1, 2, 3, 4, 5)

1
2
3
4
5
<class 'tuple'>

As can be seen from Example 2, number is indeed assigned the actual parameter (1,2,3,4,5).
Be reasonable when speaking. For details, please refer to the official python documentation. Here is a picture:
Write picture description here
Example 3:

def foo(a, *number):
    print('a:', a)
    print('number:', number)
    for i in number:
        print(i)
    print(type(number))


foo(1, 2, 3, 4, 5)

a: 1
number (2, 3, 4, 5)
2
3
4
5
<class 'tuple'>

As can be seen from Example 3, the actual parameter received by number becomes (2,3,4,5), and the first parameter 1 is replaced by the formal parameter aAccept and leave.
So here we can give the full version of what ? does:

The role of ?: When a function accepts actual parameters, they are assigned to the function parameters in order. If a formal parameter with ? is encountered, the unallocated actual parameters are packed in the form of a tuple (pack) and assigned Give the formal parameter with ?

You can verify it with a few more examples:
Example 4:

def foo(a, b, *number):
    print('a:', a)
    print('b:', b)
    print('number:', number)
    for i in number:
        print(i)
    print(type(number))


foo(1, 2, 3, 4, 5)

a: 1
b: 2
number: (3, 4, 5)
3
4
5
<class 'tuple'>

Example 5:

def foo(a, b, *number, c):
    print('a:', a)
    print('b:', b)
    print('c:', c)
    print('number:', number)
    for i in number:
        print(i)
    print(type(number))


foo(1, 2, 3, 4, 5)

Traceback (most recent call last):
  File "C:/Users/PycharmProjects/untitled10/test19.py", line 11, in <module>
    foo(1, 2, 3, 4, 5)
TypeError: foo() missing 1 required keyword-only argument: 'c'

Note that in Example 5, I specifically found an example of reporting an error. Analyze yourself why the error is reported. The answer is: The parameter in front of c contains ?, and all the remaining actual parameters are accepted. No actual parameters are passed in c!

At this point, the packaging of ? (pack) is clearly explained.
There is also a small tail left: What are args?
The answer is: args is just a conventional way of writing formal parameters. If you write it in other ways, it will be fine, but it is not conducive to unifying the form. Just like in our example, the number we have always used still runs correctly.

Split parameters

Example 6:

def bar(a,b,c):
    print(a,b,c)

bar(*[1,2,3])

1 2 3

As can be seen, ? is not used in function definition this time, but in function call. What is its role in this example?
The answer is: split (unpack) the packed actual parameters (tuple or list) into individual ones, and assign them to the formal parameters of the function in turn.
In this example, the packed actual parameter [1,2,3] is split, 1 is assigned to the formal parameter a, 2 is assigned to the formal parameter b, and 3 is assigned to the formal parameter c.

?The effect of splitting is that simple. Once you understand the principle, everything else remains the same. Ask two questions and practice:
Exercise: Which of the following three programs can run normally?
Example 7:

def bar(a,b):
    print(a,b)


bar(*[1, 2, 3])

Example 8:

def bar(a, b, c, d):
    print(a, b, c, d)


bar(*[1, 2, 3])

Example 9:

def bar(a, b, c, d=10):
    print(a, b, c, d)


bar(*[1, 2, 3])

The answer is that only example 9 works properly. Because according to the principle we talked about, the actual parameter 3 in Example 7 has no corresponding formal parameter acceptance, and the formal parameter d in Example 8 has no actual parameter assignment.

**kwargs

Packaging parameters

It will be easy to understand after learning **kwargs above.
**kwargs also consists of two parts – ** and kwargs. The point here is. Yes, kwargs is just a conventional way of writing. It has no other special meaning. You can still use it if you change it to another one. However, for the sake of code readability, it is best to use the conventional way of writing.
also has two functions – packing parameters (pack) and splitting parameters (unpack)!
But there are still differences, to put it simply:
Pack: *args is to pack multiple positional parameters into tuple, * *kwargs packages multiple keyword parameters into a dictionary.
Split (unpack): *args splits the packed parameters into individual ones and assigns them to the formal parameters of the function in turn, **kwargs is to split the key values of the dictionary into individual ones and assign them to the formal parameters of the function in turn.
Example 10

def bar(**number):
    print(number)


bar(a=1, b=2, c=3)

{<!-- -->'a': 1, 'b': 2, 'c': 3}

Split parameters

Example 11

def bar(a, b, c):
    print(a,b,c)


bar(**{<!-- -->'a': 1, 'b': 2, 'c': 3})

1 2 3

Note that there is something to note here, that is, when using the method to disassemble the dictionary and assign values to the formal parameters, the key names of the dictionary need to be consistent with the function parameters, otherwise an error will be reported. Try it yourself and you will know.

Positional parameters, keyword parameters, *args, and **kwargs must be mixed in a certain order, and I will not write them here. Because this problem itself should not be a problem, but a basic skill that should be acquired when learning functions. If you really need it, you can check it yourself to deepen your impression, which is better than just reaching out and getting it from me.

Criticisms and corrections are welcome~~~