Packing and Unpacking Arguments in Python

Challenge Inside! : Find out where you stand! Try quiz, solve problems & win rewards!

Learn via video courses

Introduction

When discussing Python functions, we introduce the terms ‘arguments’ and ‘keyword arguments'. We often see *args and **kwargs being passed as arguments during the function definition. Before jumping into the details, let us first understand the two types of function arguments in Python with the help of an example:

Python Example to Understand Types of Function Argument

Consider this python function definition that checks a candidate's eligibility to vote for elections.

  • Positional Arguments: They are the arguments to be passed to a function call in a specific order.

    Now calling the function:

This will output to:

  • Keyword Arguments: Keyword arguments are included with a keyword to the function call, which makes them identifiable with a name. Unlike positional arguments, we can pass them to a function call out of order.

Now calling the function:

This will output to:

Let us now jump to the main topic, *args and **kwargs. args in *args stands for positional arguments, and kwargs in **kwargs stands for keyword arguments.

Both \* and \*\* are the operators that perform packing and unpacking in Python. We can use the \* operator (quite often associated with args) with any iterable (such as a tuple, list, and strings), whereas the \*\* operator, (quite often associated with kwargs) can only be used on dictionaries.

The terms args and kwargs are the python programming conventions we use. Otherwise, we can use any variable. Let’s move forward to gain a better understanding and usage of packing and unpacking in Python programming.

Packing Arguments

What comes to your mind when you think of ‘packing arguments’? You got it right! It is simply ‘packing’ the arguments into a single variable (i.e., a tuple of all arguments) that a function call will receive.

It means that the elements inside a function can individually be accessed in the same way as for tuple values, i.e., tuple[0], tuple[1] etc. Furthermore, it also helps us convert the tuple into a list that can be manipulated.

Python Example to Understand Packing with * Operator

Let us discuss an example to demonstrate how we can manipulate the elements of args:

The output of the program will be:

Notice something? When we print args, all the elements we passed into the function call got stored into a tuple, and then we converted it into a list to modify the elements of the list. Now, we can easily access the elements as we do for any list.

How is packing arguments helpful? Well, we often work with functions that take in a variable number of arguments, such as functions for calculating the sum of elements in a tuple/list, etc.

For example:

In this case, executing the function add_numbers() with any number of arguments such as:
add_numbers(2,3) will return 5 or add_numbers(11, 54, 1, 2) will return 68.

Python example to understand packing with ** operator

Similarly, we can use ** to perform packing of keyword arguments that are passed to a function. In the following function, we will see how an input (in the form of key:value pairs) to our function can be packed into a dictionary.

Output of the program will be:

We printed kwargs. Notice something? Yes, all the arguments we passed as input to our function are now inside a dictionary.

We can easily access these dictionary items by:

This will output to:

Unpacking Arguments

I am sure you are guessing what this means now. Consider we have a tuple that we pass to the add_numbers() function:

It will result in an error because we passed the whole tuple into the function call. It will result in the value of args being a tuple inside a tuple.

When we iterate over args, we will get a tuple, and we cannot perform += operation with an integer (total) and a tuple (num).

Here is where unpacking comes to help. We could have had a tuple with any number of values. In such times, unpacking is used.

Both * and ** operators are used for unpacking argument values from a tuple and a dictionary, respectively.

Let us see how unpacking the arguments in the function call can help us resolve the error:

This won’t produce an error and will output 15 as our required answer!

Let is understand this further by taking more examples:

Example of unpacking with * operator:

Let us understand this by taking an example of unpacking with * operator:

Consider a basic function that takes in 3 arguments and uses them to show information about the student.

The output is:

We can clearly see how we unpacked the arguments in our tuple for putting them in the function call. The details tuple is unpacked and works as if we typed each argument separately for the function call.

NOTE: One thing to be careful with is the size of the tuple that we pass to the function as an argument. For example, the function we defined above, takes in a specific number of arguments. The size of the tuple should be equal to the number of arguments specified in the function, otherwise we face an error.

Example of unpacking with ** operator:

Let us now take an example of unpacking with ** operator: ** operator is used to unpack arguments from dictionaries and perform operations on them seamlessly.

Consider we have a dictionary. We define our function that takes three input arguments. The argument names are the same as the keys of the dictionary. This is where the ** operator will be helpful to us. We can easily unpack our dictionary during the function call without having to type out each parameter separately.

This gives the output as:

Working of Packing and Unpacking simultaneously

Now that we have discussed packing and unpacking separately, let us see how they come together. We now know that both * and ** operators perform different operations in different cases when used. When we use them while defining a function, they perform packing.

What happens is that all the arguments are packed into one single variable (specifically a tuple), which is conventionally called args. Think of it like this: ‘packing’ happens when we place multiple values corresponding to a starred variable. Additionally, if required, we can modify and delete the elements of this tuple by converting it to a list first.

So, when does all the unpacking happen? Unpacking happens when the * or ** operators are placed before a tuple or a dictionary respectively during a function call. Let us now grab some in-depth understanding of packing and unpacking with an example.

Example 1 Let us take an example of a simple Python program to determine a person’s eligibility to vote. We will input the user’s name and age to check_eligibility(), where packing takes place. Then eligibility to vote is checked for and the args is passed to the print_eligibility() function, where unpacking takes place and the eligibility message is printed.

This gives the output as:

We can understand from this example how we can access the packed arguments in the args tuple, how we can easily modify them, and how they can be passed for a method call as arguments.

**kwargs works the same, the difference being that it is used for keyword arguments that are stored in a python dictionary.

Conclusion

  • The * operator (quite often associated with args) can be used with any iterable, whereas the ** operator (quite often associated with kwargs) can only be used on dictionaries.
  • Packing and unpacking come out to be very helpful when we have functions that can take a variable number of arguments.
  • Accessing the arguments very easily helps us modify the function arguments and use them further.

Packing and Unpacking arguments in Python programming are fundamentally useful and quite a game-changer when it comes to working with functions in Python. *args and **kwargs prove to be very useful while writing better wrapper functions as they require a variable number of arguments.

See Also: