This text is part of a Real Python tutorial by Davide Mastromatteo.
Sometimes, when you look at a function definition in Python, you might see that it takes two strange arguments: *args
and **kwargs
. If you’ve ever wondered what these peculiar variables are, or why your IDE defines them in main()
, then this article is for you. You’ll learn how to use args and kwargs in Python to add more flexibility to your functions.
By the end of the lesson, you’ll know:
- What
*args
and**kwargs
actually mean - How to use
*args
and**kwargs
in function definitions - How to use a single asterisk (
*
) to unpack iterables - How to use two asterisks (
**
) to unpack dictionaries
This article assumes that you already know how to define Python functions and work with lists and dictionaries.
Passing Multiple Arguments to a Function
*args
and **kwargs
allow you to pass multiple arguments or keyword arguments to a function. Consider the following example. This is a simple function that takes two arguments and returns their sum:
def my_sum(a, b):
return a + b
This function works fine, but it’s limited to only two arguments. What if you need to sum a varying number of arguments, where the specific number of arguments passed is only determined at runtime? Wouldn’t it be great to create a function that could sum all the integers passed to it, no matter how many there are?
Using the Python args Variable in Function Definitions
There are a few ways you can pass a varying number of arguments to a function. The first way is often the most intuitive for people that have experience with collections. You simply pass a list or a set of all the arguments to your function. So for my_sum()
, you could pass a list of all the integers you need to add:
sum_integers_list.py
def my_sum(my_integers):
result = 0
for x in my_integers:
result += x
return result
list_of_integers = [1, 2, 3]
print(my_sum(list_of_integers))
This implementation works, but whenever you call this function you’ll also need to create a list of arguments to pass to it. This can be inconvenient, especially if you don’t know up front all the values that should go into the list.
This is where *args
can be really useful, because it allows you to pass a varying number of positional arguments. Take the following example:
sum_integers_args.py
def my_sum(*args):
result = 0
# Iterating over the Python args tuple
for x in args:
result += x
return result
print(my_sum(1, 2, 3))
In this example, you’re no longer passing a list to my_sum()
. Instead, you’re passing three different positional arguments. my_sum()
takes all the parameters that are provided in the input and packs them all into a single iterable object named args
.
Note that args
is just a name. You’re not required to use the name args
. You can choose any name that you prefer, such as integers
:
sum_integers_args_2.py
def my_sum(*integers):
result = 0
for x in integers:
result += x
return result
print(my_sum(1, 2, 3))
The function still works, even if you pass the iterable object as integers
instead of args
. All that matters here is that you use the unpacking operator (*
).
Bear in mind that the iterable object you’ll get using the unpacking operator *
is not a list
but a tuple
. A tuple
is similar to a list
in that they both support slicing and iteration. However, tuples are very different in at least one aspect: lists are mutable, while tuples are not. To test this, run the following code. This script tries to change a value of a list:
change_list.py
my_list = [1, 2, 3]
my_list[0] = 9
print(my_list)
The value located at the very first index of the list should be updated to 9
. If you execute this script, you will see that the list indeed gets modified:
$ python change_list.py
[9, 2, 3]
The first value is no longer 0
, but the updated value 9
. Now, try to do the same with a tuple: