How to use an Undefined Number of Arguments in Python using *args and **kwargs


Python offers flexibility when it comes to handling function arguments. Two powerful features, *args and **kwargs, provide a way to work with a variable number of arguments in a function. In this blog, we'll explore these concepts, understand their applications, and see how they contribute to the versatility of Python functions.

In this article, we will cover what ** (double star/asterisk) and * (star/asterisk) do for parameters in Python—allowing us to pass an undefined number of arguments to a function.

Special Symbols Used for passing arguments in Python:

  • *args (Non-Keyword Arguments)

  • **kwargs (Keyword Arguments)

Understanding *args (Arbitrary Arguments)

The term *args may initially seem mysterious, but it is a straightforward concept. In Python, *args allows a function to accept any number of positional arguments. The asterisk (*) symbol before the argument name is a signal to gather all the positional arguments into a tuple.

def example_function(*args):
    for arg in args:
        print(arg)

example_function(1, 2, 3, 'four')

In this example, example_function takes any number of arguments and prints each one. When called with four arguments, it outputs:

1
2
3
four

The flexibility of *args becomes apparent when you want a function to handle an unpredictable number of inputs without explicitly specifying each parameter.

Unpacking Lists and *args

*args is not limited to function definitions; it can also be used for unpacking elements from a list or tuple into individual arguments when calling a function.

def sum_values(a, b, c):
    return a + b + c

values = [1, 2, 3]

result = sum_values(*values)
print(result)

In this case, *values unpacks the list into individual arguments, resulting in the equivalent of sum_values(1, 2, 3).

Understanding **kwargs (Key Word Arguments)

While *args deals with positional arguments, **kwargs is designed for handling keyword arguments. The double asterisk (**) signals that the function can accept any number of keyword arguments, gathering them into a dictionary.

def example_function(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

example_function(name='John', age=25, city='Example City')

Executing this function with keyword arguments produces the following output:

name: John
age: 25
city: Example City

Combining *args and **kwargs

The true power of these features shines when they are used together in a function signature. This allows the function to accept both positional and keyword arguments, providing unparalleled flexibility.

def flexible_function(*args, **kwargs):
    print("Positional arguments:")
    for arg in args:
        print(arg)

    print("\nKeyword arguments:")
    for key, value in kwargs.items():
        print(f"{key}: {value}")

flexible_function(1, 2, 3, name='John', age=25)

In this example, flexible_function gracefully handles a mix of positional and keyword arguments. This can be incredibly useful in situations where you want to create versatile and adaptable functions, and can be especially useful when paired with decorators ands wrapper functions.

Conclusion

In the world of Python, *args and **kwargs are designed for flexibility and can be utilized to make easily adaptable functions. Whether you're dealing with an uncertain number of inputs or striving for a more adaptable function interface, these features empower you to write code that is both concise and versatile. Embrace the simplicity of *args and the expressiveness of **kwargs to make your Python functions more robust and accommodating to various use cases.