Examples of Using *args and **kwargs in Python

Yes, I know, you have also seen them in a lot of examples but it is not clear to you what the *args and **kwargs parameters mean in a Python function. And not only as parameters, but can also be passed as arguments. In this post I want to explain what they mean and when to use them.

“All the examples have been implemented using Python 3.0”

In high-level programming languages, Python among them, when declaring a function we can define a series of parameters with which to invoke said function. As a general rule, the number and name of these parameters is immutable. 

However, there are situations where it is much more appropriate for the number of parameters to be optional and/or variable.

What *args and **kwargs mean as Parameters

Understanding *args

In Python, the special parameter *args in a function is used to optionally pass a variable number of positional arguments .

Hahaha, what a definition paranoia. Let’s see it in detail:

  • What really indicates that the parameter is of this type is the symbol ‘*’, the name args is used by convention.
  • The parameter receives the arguments as a tuple.
  • It is an optional parameter. The function can be called using it, or not.
  • The number of arguments when calling the function is variable.
  • They are positional parameters, so unlike named parameters, their value depends on the position at which they are passed to the function.

But as I always say, things look better with an example:

The following function takes two parameters and returns their sum as a result:

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

If we call the function with the values x=2 and y=3 , the returned result will be 5.

>>> sum(2, 3)
5

But, what happens if we later decide or realize that we need to add one more value?

>>> sum(2, 3, 4)

Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: sum() takes 2 positional arguments but 3 were given

Obviously, it was clear that the function call was going to fail.

How can we solve this problem? Well, one option would be to add more parameters to the function, but how many?

The best, most elegant, and most Python-like solution is to make use of *args in the definition of this function. In this way, we can pass as many arguments as we want. But before this, we have to reimplement our sum function :

def sum(*args):
    value = 0
    for n in args:
        value += n
    return value

With this new implementation, we can call the function with any variable number of values:

>>> sum()
0

>>> sum(2, 3)
5

>>> sum(2, 3, 4)
9

>>> sum(2, 3, 4, 6, 9, 21)
45

Understanding **kwargs

Let’s now look at the use of **kwargs as a parameter.

In Python, the special parameter **kwargs in a function is used to optionally pass a variable number of named arguments .

The main differences from *args are:

  • What really indicates that the parameter is of this type is the symbol ‘**’, the name kwargs is used by convention.
  • The parameter receives the arguments as a dictionary.
  • Since this is a dictionary, the order of the parameters does not matter. Parameters are associated based on dictionary keys.

When is its use useful?

Let’s imagine that we want to implement a filter function that returns an SQL query of a customer table that has the following fields: name, surname, date_registration, city, province, type and date_of birth.

A first approximation could be the following:

def filter (city, province, date_registration) : 
    return "SELECT * FROM customers WHERE city='{}' AND province='{}' AND date_registration={};" . format (city, province, date_registration)

It is not a function to feel very happy Among the different problems that may arise we have:

  • If we want to filter by a new parameter, we have to change the function definition as well as the implementation.
  • The parameters are all required.
  • If we want to query other types of clients maintaining this query, we must create a new function.

The solution to all these problems is to make use of the **kwargs parameter . Let’s see what the new filter function would look like using **kwargs :

def filter ( **kwargs ) : 
    query = "SELECT * FROM customers"
    i = 0
    for key, value in kwargs. items () :
        if i == 0 :
            query += " WHERE "
        else :
            query += " AND "
        query += "{}='{}'" . format ( key, value )
        i += 1
    query += ";"
    returnquery _

With this new implementation we have solved all our problems like true pythonists.

Here’s how the new filter function behaves :

>>> filter ()
SELECT * FROM customers;
>>> filter ( city= "Kolkata" )
SELECT * FROM customers WHERE city= 'Kolkata' ;
>>> filter ( city= "Kolkata" , date_registration= "25-10-2018" )
SELECT * FROM clients WHERE city= 'Kolkata' AND date_registration= '05-02-2022' ;

So far we’ve seen what the *args and **kwargs parameters mean in a Python function and two examples of when and how to use them. Other common usage examples are decorators (which I’ll tell you about in another post) and the __init__ method in inheritance. I will show you the latter as it makes a combined use of both.

Suppose we have the following Point class :

class Point:
    define __init__ ( self , x= 0 , y= 0 ) : 
        self.x = x
        self.y = y
    def __repr__ ( self ) : 
        return "x: {}, y: {}" . format ( self.x, self.y )

And now we want to add a Circle class that inherits from Point . To know all the details of the circle, we need to know its radius, so it must be included in the __init__ method . However, the definition will be a bit different than for the Point class :

class Circle ( Point ) : 
    def __init__ ( self, radius, *args, **kwargs ) : 
        self.radius = radius
        super () . __init__ ( *args, **kwargs )
    def __repr__ ( self ) : 
        return "x: {}, y: {}, radius: {}" . format ( self.x, self.y, self.radius )

With this implementation, if the definition of the __init__ method in the Point class changes, we won’t have to modify the implementation of the Circle class.

To create an object of the Circle class, just write:

>>> Circle ( 10 , 1 , 1 )
x: 1 , y: 1 , radius: 10

In other posts we will see more about inheritance and object orientation in the Python language. With this example I simply wanted to show another use of the *args and **kwargs parameters .

Order Matters

I want to mention that the order of the *args and **kwargs parameters in a function definition matters , a lot. Both can appear together or individually, but always at the end and in the following way:

def example ( arg1, arg2, *args, **kwargs )

*args and **kwargs as arguments in a function call

*args and **kwargs can also be used as arguments when calling a function and their behavior is different from what I have shown you above.

Imagine the following result function :

def result ( x, y, op ) : 
    if op == '+' :
        return x + y
    elif op == '-' :
        return x - y

This function takes three parameters: x , y and op and can be called in different ways. The first one is the one you already suppose:

>>>result(1, 2, '+')
3

But we can also call the  result function with a single parameter of type iterable, such as a tuple or a list, as follows (*args) :

>>> a = (1, 2, '+')
>>> result(*a)
3

Or even like this:

>>> a = (2, '-')
>>> result(3, *a)
1

We can also pass a dictionary as an argument using the parameter names (**kwargs) as keys :

>>> a = {"op": "+", "x": 2, "y": 5}
>>> result(**a)
7

Conclusion

Well, that brings to an end the tutorial on what they mean and how and when to use the *args and **kwargs parameters in Python. Let’s review the key issues:

  • Use *args to optionally pass a variable number of positional arguments to a function.
  • The *args parameter receives the arguments as a tuple.
  • Use **kwargs to optionally pass a variable number of named arguments to a function.
  • The **kwargs parameter receives the arguments as a dictionary.

Finally, I want to tell you that with great power comes great responsibility. Using *args and **kwargs can save you a lot of headaches and make you a top programmer, but it can also lead to unexpected results if you’re not careful with their use.