args and kwargs in Python Functions for Variable Number of Arguments

Introduction

Variable arguments, which are commonly known as args and kwargs in Python, enable you to create functions that can handle a dynamic number of inputs.

Variable Positional Arguments (args)

Suppose you’d like to write a function that returns the sum of a few numbers.

In order to be able to sum up any number of numbers, you could create a function that takes a single argument that contains an iterable, such as a list.

This way, you can implement the function with a predetermined number of arguments – just one, an iterable – but be able to sum up a group of numbers of any size.

Example:

def calculate_sum(numbers):
    total = 0
    for num in numbers:
        total += num
    return total

(Note that Python has a built-in function called sum that does this, so you wouldn’t actually need to write this function, but it’s a good demonstration.)

numbers = [1,2,3,4,5]
total = calculate_sum(numbers)
print(total)

Output:

15

This function works, but you don’t always have numbers in a list (or another iterable), therefore it might be convenient to have a function that takes separate arguments and adds them up.

Example:

x = 1
y = 2
z = 3

total = flexible_sum(x, y, z)
print(total)

Output:

6

You can define such a function by placing an asterisk * in front of a parameter in the function’s definition.

The parameter can have any valid name, but args is often used by common convention.

Example:

def flexible_sum(*args):
    total = 0
    for num in args:
        total += num
    return total

The above code is the implementation of the flexible_sum() function from the previous example.

The asterisk in front of the args parameter tells Python to accept zero or more arguments and put them into a tuple collection.

The implementation iterates over the tuple with a for loop and sums all the numbers

The following example demonstrates that args is indeed a tuple by printing its content and class name.

Example:

def args_demo(*args):
    print(f"The argument has {args} in it and it's a {args.__class__.__name__}.")

args_demo(1, 2, 3, 4, 5)

Output:

The argument has (1, 2, 3, 4, 5) in it and it's a tuple.

Mixing Regular Arguments With Variable Positional Arguments

It’s straightforward to define a function that takes regular arguments followed by variable positional arguments.

Example:

def flexible_sum2(a, b, *args):
    total = a + b
    for num in args:
        total += num
    return total

The flexible_sum2() function above is designed to require a minimum of two numbers by explicitly defining two positional parameters, a and b, to the left of *args.

Calling it with two or more arguments produces the normal result.

Example:

flexible_sum2(1, 2, 3, 4, 5)

Output:

15

If you pass just one argument, you will get an error, but it’s what we want in this case.

Example:

flexible_sum2(10)

Output:

TypeError: flexible_sum2() missing 1 required positional argument: 'b'

Functions with regular arguments followed by variable positional arguments are also possible but require some caution.

The following function calculates a total price for an item by subtracting any number of discount coupons from a base price and applying tax at the end.

def calculate_total_price(base_price, *discounts, tax_rate):
    total_price = base_price
    for discount in discounts:
        total_price -= discount
        
    total_price *= (1 + tax_rate/100)
    return total_price

As you can see, the tax_rate parameter follows the variable argument parameter *discounts.

Let’s say the base price is $100.00, you’ve got two coupons for $5 and $20 each, and the tax rate is 10%.

Example:

calculate_total_price(100.00, 5, 20, 10.0)

Output:

TypeError: calculate_total_price() missing 1 required keyword-only argument: 'tax_rate'

That function call produces an error because *discounts consumes all the rest of the arguments, including the intended tax rate, and discount ends up being (5, 20, 10.0), while tax_rate is left unassigned.

tax_rate can only be used in the function call as a keyword argument. That’s why the error message says it’s “missing 1 required keyword-only argument”.

The next example has the problem fixed.

Example:

calculate_total_price(100.00, 5, 20, tax_rate=10.0)

Output:

82.5

Variable Number of Keywords Arguments (kwargs)

In addition to a variable number of positional arguments, Python also supports a variable number of keyword arguments. This can be accomplished by placing a double asterisk ** in front of the parameter designated to accept the keyword arguments.

You can give the variable keyword parameter any name (just like with the variable positional parameter), and by convention, it is commonly named kwargs.

Inside the function, it’s assigned a dictionary that holds the keywords and values obtained from the argument in the function call.

Example:

def display_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")
        
display_info(name="Alice", age=30, city="Wonderland")

Output:

name: Alice
age: 30
city: Wonderland

In the example above, kwargs is assigned the dictionary {'name': 'Alice', 'age': 30, 'city': 'Wonderland'} when the function is called. The function iterates through all the key, value-pairs with the for loop and prints them individually.

Mixing Regular Arguments With Variable Keyword Arguments

Suppose you would like to add a header and footer to the information displayed with mandatory positional arguments. Let’s rewrite the example above to include those.

Example:

def display_info(header, footer, **kwargs):
    print(header)
    for key, value in kwargs.items():
        print(f"{key}: {value}")
    print(footer)
        
display_info("<<<<<<<<<<>>>>>>>>>>", "--------------------", name="Alice", age=30, city="Wonderland")

Output:

<<<<<<<<<<<<>>>>>>>>>>>
name: Alice
age: 30
city: Wonderland
--------------------

The variable keyword parameter can come after any number of positional parameters. In the example above, **kwargs comes after the header and footer positional parameters.

However, if the variable keyword parameter is used, it has to be the last parameter in the parameter list. It can’t be followed by any other parameters like the variable positional parameter can. The next example will produce a syntax error.

Example:

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

Finally, what happens when a regular argument is specified with a keyword? Then the regular argument is assigned that value. It does not appear as a keyword in the variable keyword argument’s dictionary.

Example:

display_info(name="Alice", age=30, city="Wonderland", header="<<<<<<<<<<<<>>>>>>>>>>>", footer="--------------------")

Output:

<<<<<<<<<<<<>>>>>>>>>>>
name: Alice
age: 30
city: Wonderland
--------------------

In the example above, header and footer are passed as keyword arguments, but kwargs still only gets the name, age, and city, as you can see in the output.

Mixing Regular Arguments with Variable Positional and Variable Keyword Arguments

You can also define functions that have a mix of regular parameters and both types of variable parameters, positional and keyword.

Example:

def mixed_arguments(name, *args, **kwargs):
    print(f"Name: {name}")
    print("Additional arguments:")
    for arg in args:
        print(arg)
    print("Additional keyword arguments:")
    for key, value in kwargs.items():
        print(f"{key}: {value}")

mixed_arguments("Alice", 1, 2, 3, age=20, city="Wonderland")

Output:

Name: Alice
Additional arguments:
1
2
3
Additional keyword arguments:
age: 20
city: Wonderland

The *args, **kwargs Parameters

The *args, **kwargs parameter combination, with no other parameters, can be used to create a function that accepts any syntactically correct list of arguments. If you see a function that has those parameters, you can immediately recognize it for what it is.

Example:

def flexible_function(*args, **kwargs):
    print("I can accept any legal arguments!")
    print(f"args = {args}")
    print(f"kwargs = {kwargs}")
    
flexible_function(1,2,3,4, one=1, two=2, three=3, four=4)

Output:

I can accept any legal arguments!
args = (1, 2, 3, 4)
kwargs = {'one': 1, 'two': 2, 'three': 3, 'four': 4}

This type of construct is typically used within functions that create other functions, such as decorators.

Summary & Reference for Variable Arguments Reference

Variable arguments in Python functions allow the creation of dynamic and versatile functions that can handle different numbers of inputs.


To handle a variable number of positional arguments, place an asterisk * before the parameter name. Python will assign a tuple with all the values to the argument by that name. By convention, the name commonly used for this parameter is args.

def flexible_sum(*args):
    total = 0
    for num in args:
        total += num
    return total
    
flexible_sum(1, 2, 3, 4, 5)  # --> 15

Mixing regular positional arguments with variable positional arguments is possible. The function in this example has two regular positional arguments followed by some variable positional arguments defined by *args:

def flexible_sum2(a, b, *args):
    total = a + b
    for num in args:
        total += num
    return total
    
flexible_sum2(1, 2, 3, 4, 5)  # --> 15

Regular arguments can be followed by variable positional arguments but must be passed as keyword arguments.

def calculate_total_price(base_price, *discounts, tax_rate):
    total_price = base_price
    for discount in discounts:
        total_price -= discount

	total_price *= (1 + tax_rate / 100)
	return total_price

calculate_total_price(100.00, 5, 20, tax_rate=10.0)  # --> 82.5

To handle a variable number of keyword arguments, place a double asterisk ** before the parameter name. Python will assign a dictionary with all the keyword-values pairs to the argument by that name. By convention, the name commonly used for this parameter is kwargs.


def display_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")
        
display_info(name="Alice", age=30, city="Wonderland")

The variable keyword parameter, can come after any number of positional parameters, and must the last one in the parameter list.

def display_info(header, footer, **kwargs):
    print(header)
    for key, value in kwargs.items():
        print(f"{key}: {value}")
    print(footer)
        
display_info("<<<<<<<<<<>>>>>>>>>>", "--------------------", name="Alice", age=30, city="Wonderland")

Mixing regular arguments with both types of variable parameters (positional and keyword) is also possible.

def mixed_arguments(name, *args, **kwargs):
    print(f"Name: {name}")    
    for arg in args:
        print(arg)    
    for key, value in kwargs.items():
        print(f"{key}: {value}")

mixed_arguments("Alice", 1, 2, 3, age=20, city="Wonderland")

The *args, **kwargs parameter combination with no other parameters is often used for writing functions that can accept any syntactically correct list of arguments.

def flexible_function(*args, **kwargs):
    print("I can accept any legal arguments!")
    print(f"args = {args}")
    print(f"kwargs = {kwargs}")
    
flexible_function(1,2,3,4, one=1, two=2, three=3, four=4)