Args and kwargs in Python

Python args And kwargs Explained: All You Need to Know

Using args and kwargs can be quite cryptic especially if you are new to Python. Let’s find out together all you need to know about them.

What are *args and **kwargs?

*args and **kwargs allow to pass an arbitrary number of positional arguments (*args) and keyword arguments (**kwargs) to a Python function. You can use *args and **kwargs to make your Python code more flexible.

We will start with a simple example that will show you quickly how to use *args and **kwargs. Then we will got through more examples to make sure you have a full understanding of them.

We will also see where else you can use *args and **kwargs…

Let’s get started!

An Example of How to Use *args With a Function

Let’s create a simple function that calculates the sum of two numbers:

def sum(x, y):
    result = x + y
    return result

Then we will call it with one, two or three arguments to see what happens:

One argument

print(sum(1))

We get back the following error because the sum function expects two arguments and we have only passed one:

Traceback (most recent call last):
   File "/opt/python/codefather/args_kwargs.py", line 25, in 
     print(sum(1))
 TypeError: sum() missing 1 required positional argument: 'y'

Notice how the Python interpreter is telling us that 1 required positional argument (y) is missing.

You will see soon how positional arguments are related to *args

Two arguments

print(sum(1, 2))

This time there is no error. Our program prints the correct sum because we pass the correct number of arguments when we call the sum function.

Thee arguments

print(sum(1, 2, 3))

We get back an error because our function doesn’t know how to handle three arguments:

Traceback (most recent call last):
   File "/opt/python/codefather/args_kwargs.py", line 25, in 
     print(sum(1, 2, 3))
 TypeError: sum() takes 2 positional arguments but 3 were given

We can solve this problem with *args…

What is *args?

*args allows to pass a variable number of positional arguments to a Python function. It’s accessible as tuple in the function itself.

Let’s make few changes to our sum function:

  • Add *args as last argument in the signature of the function.
  • Check if args is empty and if it’s not empty add every argument in *args to the final result.
def sum(x, y, *args):
    print("args value:", args)
    result = x + y

    if args:
        for arg in args:
            result += arg

    return result

When we execute this code we get 6 as result and we can also see the content of the *args tuple:

>>> print(sum(1, 2, 3))
args value: (3,)
6

Tuples are similar to Python lists with the only difference that they are immutable.

How to Pass **kwargs To a Function

Now that we have seen how *args works we will move to **kwargs.

What is **kwargs?

**kwargs allows to pass a variable number of keyword arguments to a Python function. Its content is available as dictionary in the function itself.

Let’s see how it applies to our sum function…

def sum(x, y, *args, **kwargs):
    print("args value:", args)
    print("kwargs value:", kwargs)

    result = x + y

    if args:
        for arg in args:
            result += arg

    if kwargs:
        for kwarg in kwargs.values():
            result += kwarg

    return result

We have updated the sum function to:

  • Accept the **kwargs parameter.
  • Print the content of the kwargs dictionary.
  • Add any values in **kwargs to the final sum.

When we call the function:

print(sum(1, 2, 3, number1=6, number2=8))

The output is the following:

args value: (3,)
kwargs value: {'number1': 6, 'number2': 8}
20

You can see the content of the kwargs dictionary and the sum of all the numbers that is 20.

Can you see how **kwargs works?

Now…

To make the sum function generic we could remove x and y, and just keep *args and **kwargs as function arguments. Give it a try!

I will include the full source code at the end of this tutorial.

And now let’s move to something else…

Why Using the Names *args And **kwargs

Based on what we have seen so far you might think that the Python interpreter recognises exactly the names *args and **kwargs.

Let’s do a little experiment…

I have changed our sum function, do you see the difference?

def sum(x, y, *custom_args, **custom_kwargs):
    print("args value:", custom_args)
    print("kwargs value:", custom_kwargs)

    result = x + y

    if custom_args:
        for arg in custom_args:
            result += arg

    if custom_kwargs:
        for kwarg in custom_kwargs.values():
            result += kwarg

    return result

I have changed *args and **kwargs respectively to *custom_args and **custom_kwargs.

Run the new program, you will see that the function still works as it was working before.

So, it looks the names args and kwargs don’t really matter

…so how does the Python interpreter know how to deal with *custom_args and **custom_kwargs?

Let’s apply another little change…

def sum(x, y, args, kwargs):
    ...
    ...

I have removed * and ** from args and kwargs. And the output becomes:

Traceback (most recent call last):
   File "/opt/python/codefather/args_kwargs.py", line 37, in 
     print(sum(1, 2, 3, number1=6, number2=8))
 TypeError: sum() got an unexpected keyword argument 'number1'

This shows that…

What makes the Python interpreter understand how to handle *args and **kwargs is simply the * and ** before the parameter names. That’s why you can use any names you want. Using the names args and kwargs is just a convention to make code easier to read for all developers.

The * is called iterable unpacking operator and the ** is the dictionary unpacking operator.

Later on in this tutorial we will look deeper at what it means that * and ** are unpacking operators…

Passing *args And **kwargs to a Function

We will try something different now, to show why we say that *args contains positional arguments and **kwargs contains keyword arguments.

Let’s go back to the first version of our sum function:

def sum(x, y):
    result = x + y
    return result

This time we want to use *args and **kwargs to pass arguments to this function.

Here’s how…

Passing arguments using *args

We will define a tuple that contains the values of x and y. Then we will pass it as *args:

args = (1, 2)
print(sum(*args))

Give it a try and confirm the result is correct.

In this case the values in the *args tuple are assigned to the x and y arguments based on their position. The first element in the tuple is assigned to x and the second element to y.

If we add a couple of print statements to the sum function to see the values of x and y…

def sum(x, y):
    print("x:", x)
    print("y:", y)
    result = x + y
    return result

…we get the following result:

x: 1
y: 2
3

Passing arguments using **kwargs

We will define a dictionary that contains x and y as keys. To show that in this case the position (or order) of the dictionary element doesn’t matter, we will specify the key y before the key x.

Then we will pass it as **kwargs to our sum function:

kwargs = {'y': 2, 'x': 1}
print(sum(**kwargs))

If you execute the code you will see that once again the result is correct.

The values in the **kwargs dictionary are assigned to the x and y arguments based on their name. The value for the dictionary key ‘x’ is assigned to the x parameter of the function and the same applies to y.

The two print statements we have added before to the function confirm that the values of x and y are the ones we expect:

x: 1
y: 2
3

You can see how **kwargs can be quite a handy way to pass arguments to a function after creating a dictionary that contains the arguments required.

What is the Type For *args And *kwargs?

We have talked about the fact that *args is a tuple and **kwargs is a dictionary.

But first, let’s not take this for granted…

…we will use the Python type() function to verify their type.

def print_args_type(*args, **kwargs):
    print("*args type:", type(args))
    print("**kwargs type:", type(kwargs))

print_args_type(1, 3, number=5)

Here is the output:

*args type: <class 'tuple'>
**kwargs type: <class 'dict'>

So, what we have seen so far is confirmed when it comes to the type of *args and **kwargs.

And now let’s have a look at something that could cause syntax errors in your Python code…

Arguments Order For a Python Function

When using *args and **kwargs together with standard function positional or keyword arguments there’s something you have to be aware…

To show this concept I have modified the function we have created in the previous section:

def print_args_type(*args, **kwargs, arg1):
    print("*args type:", type(args))
    print("**kwargs type:", type(kwargs))
    print("arg1:", arg1)

print_args_type(1, 3, number=5, 6)

Here are the changes I have applied to our program:

  • arg1 added at the end of the parameters list of the print_args_type() function.
  • print statement added to the function to print the value of arg1.
  • additional argument (6) passed when we call print_args_type().

In theory we would expect the new argument passed in the function call to be assigned to the parameter arg1, but this is what we get back:

File "/opt/python/codefather/args_kwargs.py", line 48
     def print_args_type(*args, **kwargs, arg1):
                                             ^
 SyntaxError: invalid syntax

For some reason the Python interpreter doesn’t like arg1.

But why?

That’s because Python enforces a specific order for function arguments:

  • Positional arguments go first in the following order: standard arguments followed by *args.
  • Keyword arguments should come after positional arguments in the following order: standard arguments followed by **kwargs.

This means that arg1 has to go before *args.

Let’s see if it’s true, here is the updated function. Notice how I have also changed the position of the argument 6 in the call to the function:

def print_args_type(arg1, *args, **kwargs):
    print("arg1:", arg1)
    print("*args:", args)
    print("**kwargs:", kwargs)

print_args_type(6, 1, 3, number=5)

And here is the output:

arg1: 6
*args: (1, 3)
**kwargs: {'number': 5}

Python assigns the number 6 to arg1, the remaining two positional arguments (1 and 3) to *args and the only keyword argument to **kwargs.

Let’s add also a fixed keyword argument that as explained before has to go between *args and **kwargs in the function signature:

def print_args_type(arg1, *args, kw_arg1, **kwargs):
    print("arg1:", arg1)
    print("*args:", args)
    print("kw_arg1:", kw_arg1)
    print("**kwargs:", kwargs)

print_args_type(6, 1, 3, kw_arg1=4, number=5)

And the output matches what we expect:

arg1: 6
*args: (1, 3)
kw_arg1: 4
**kwargs: {'number': 5}

Makes sense?

Python Unpacking Operators Explained

At some point in this tutorial I have mentioned that * and ** are unpacking operators.

But, what does it mean in practice?

Let’s start by printing the value of a tuple without and with the * operator:

args = (1, 2, 3)
print(args)
print(*args)

The output is:

(1, 2, 3)
1 2 3

So, without the unpacking operator we see that the full tuple gets printed…

…when we apply the unpacking operator * the elements of the tuple are “unpacked”. The print statement prints three individual numbers.

This means that each one of the three numbers is a separate argument for the print() function when we apply the * operator.

The * unpacking operator applies to iterables (e.g. tuples and list).

Now, let’s have a look at what happens when we apply the * operator to a dictionary:

args = {'arg1': 1, 'arg2': 2}
print(args)
print(*args)

The output is:

{'arg1': 1, 'arg2': 2}
arg1 arg2

We only get back the keys of the dictionary back when we apply the * operator, this is not really what we want.

To unpack dictionaries you should use the ** operator.

Here is an example of how to use the ** operator to create a new dictionary that contains all the key / value pairs from two dictionaries:

dict1 = {'arg1': 1, 'arg2': 2}
dict2 = {'arg3': 3, 'arg4': 4}
print({**dict1, **dict2})

The resulting Python dictionary is:

{'arg1': 1, 'arg2': 2, 'arg3': 3, 'arg4': 4}

Now you know how unpacking operators can help you in your Python programs.

Using *args With a Python Lambda Function

Let’s go back to our initial sum function:

def sum(x, y):
    result = x + y
    return result

Below you can see the lambda representation of this function:

>>> lambda x,y : x+y
<function <lambda> at 0x101dab680> 

And here is how you can pass two arguments to this lambda:

>>> (lambda x,y : x+y)(1,2)
3 

Now, let’s say we want to pass an arbitrary number of arguments to our lambda function

…we can do it using *args:

>>> (lambda *args: sum(args))(1,2,4,5,6)
18 
>>> (lambda *args: sum(args))(3,7,6)
16 

I have written a full article about Python lambdas if you are interested in learning how to use them.

One last thing you can use *args and **kwargs for…

Args and Kwargs as Part of a Decorator

*args and **kwargs are also very useful to define decorators in Python.

You can use them in the definition of a wrapper function inside your decorator, with the aim of passing through any arguments to the function that is decorated.

Here is an example of code:

def verify_user(func):
    def wrapper(*args, **kwargs):
        if not user['logged_in']:
            print("ERROR: User {} is not logged in!".format(user['name']))
            return
        else:
            print("INFO: User {} is logged in".format(user['name']))
            return func(*args, **kwargs)

    return wrapper

I have created an article about Python decorators if you want to learn how decorators wok and how to use *args and **kwargs as part of decorators.

Conclusion

We have learned together how you can pass a variable number of arguments to Python functions using *args and **kwargs.

To recap what we have covered:

  • *args allows to pass a variable number of positional arguments to a function. This is a tuple that contains the arguments in the order in which they are passed in the function call.
  • **kwargs allows to pass a variable number of keyword arguments to a function. This is a dictionary, that’s why we talk about keyword arguments. Because arguments are identified by a key.

What matters in the name of these parameters are the * and the **. The actual names args and kwargs are not enforced by the Python interpreter and can be replaced with anything you want.

The main point of using *args and **kwargs is creating flexible functions that can adapt to multiple use cases.

Consider, for example, a function that calculates the sum of two numbers as opposed as the sum of any numbers. And this concept can be extended to anything you want to do with your Python code.

Finally, we have also:

  • looked at the order expected by the Python interpreter for positional and keyword arguments.
  • practiced the use of the unpacking operators * and **.

And now it’s time for you to start using *args and **kwargs…

…if you have any questions please let me know in the comments below.

Share knowledge with your friends!

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *