Working with Args and Kwargs in Python

Working with optional Arguments and Keyword Arguments in Python functions allows you to pass additional arguments into your Python functions.
Andrew Wood  •   01 July 2022
Andrew Wood  •   Last Updated: 01 July 2022
Working with optional Arguments and Keyword Arguments in Python functions allows you to pass additional arguments into your Python functions.

Introduction

Functions are a fundamental part of any programming language as they allow you to start with a set of inputs, perform some manipulation on those inputs, and return an output. Simple functions take a defined number of inputs and return an output. But what happens if you need to write a function where the number of inputs may vary or you wish to add some optional arguments to your function?

This is the perfect use case for providing the function with a set of *args (optional arguments) and/or **kwargs (optional keyword arguments). Don't be intimidated by the strange looking syntax - *args and **kwargs are actually quite easy to work with once you know how.

The Structure of a Function

Before we dive into the application of adding optional arguments and keyword arguments into a function, let's first review how functions are structured.

Basic Function (Number of Arguments Known)

A basic function is designed to take a number of arguments (inputs or parameters when calling the function) and return a defined output. This means that if you define a function to add two numbers, you need to provide two numbers as an input (positional arguments) or else the function will return an error.

def sumtwo(number1,number2):
    return number1 + number2
>>> print(sumtwo(3,4))
7

>>> print(sumtwo(3,4,5))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_8456\1494423628.py in <module>
      3 
      4 print(sumtwo(3,4))
----> 5 print(sumtwo(3,4,5))

TypeError: sumtwo() takes 2 positional arguments but 3 were given

It would be very time consuming (and poor programming technique) to write different functions to sum different numbers of arguments. One way to get around this is to write a function that takes a list as an input and returns the sum of the list. This however requires that you create the list before passing it to the function. By using the *args syntax we can avoid this, and create a general function that can sum as many arguments as we pass to it when the function is called.

A more Complete Function Structure

A Python function is structured as follows:

def function_name(arg1,arg2,*args,**kwargs):
    # perform some manipulation
    return result
  • arg1 and arg2 are required arguments and must be present when calling the function. There is no limit to the number of required arguments (and no requirement that there by any arguments at all).
  • Any additional arguments are added after the required arguments and indicated in the function definition by a single asterisk *.
  • Any additional keyword arguments are added after the required and additional arguments and indicated in the function by a double asterisk **

Difference Between Arguments and Keyword Arguments

It's important to understand the difference between arguments and keyword arguments in the context of a function. All functions take arguments as inputs, the difference between an argument and a keyword argument lies in how these argument parameters are called. 

  • Calling arguments without a parameter name requires that the correct calling order be maintained
  • Calling arguments by their name (keyword) means that they can be called in any order.

This is best explained by a simple example. We'll take two parameters (firstname and lastname) and return a string of the person's full name. 

def printname(firstname, lastname):
    return f"My name is {firstname} {lastname}."

Calling Parameters without Keywords

If you call functions without using keywords then the order in which the parameters are called becomes very important.

>>> result1 = printname('Pete','Mitchell') # function called using arguments
>>> print(result1)
'My name is Pete Mitchell.'

>>> result2 = printname('Mitchell','Pete') 
>>> print(result2) # incorrect output as first and last name reversed
'My name is Mitchell Pete.'

Calling Parameters with Keywords

If you call functions using keywords then the order in which the parameter is called does not matter.

>>> result3 = printname(lastname='Mitchell',firstname='Pete') # function called using keyword arguments
>>> print(result3)
'My name is Pete Mitchell.'

Make sure you are comfortable with the distinction between arguments and keyword arguments in the context of calling functions before moving onto the next section.

Using *args to Pass Multiple Arguments to a Function

If you simply need to pass an undetermined or variable number of arguments into a function (where the function is written such that the parameters are position indepentent) then you can do so using the *args syntax in the creation of the function. The easiest way to illustrate this is through an example.

Lets write a simple function to sum any number of numbers together. I've added a print statement within the function that prints out the optional arguments as they appear to the function. 

def sumof(*args):
    total = 0
    print(f"The args in function are: {args}")
    for arg in args:
        total += arg
    print(f"The total is: {total}.")
    return total

The optional arguments are accessed in the function through the variable args which is a tuple. Since we have used the *args syntax when creating the function, we can add any number of arguments into the function call.

The following are all valid calls:

>>> sumof(1,4,2)
'The args in function are: (1, 4, 2)'
'The total is: 7.'
>>> sumof(1,3,5,66,3.3)
'The args in function are: (1, 3, 5, 66, 3.3)'
'The total is: 78.3.'
>>> sumof()
'The args in function are: ()'
'The total is: 0.'

Using **kwargs to Pass Keyword Arguments

When you pass keyword arguments to a function using the **kwargs syntax, the optional keyword arguments are passed into the function as a dictionary with the keyword name as the key and the argument value as the value. We'll illustrate this concept by modifying the sum function used in the argument example above before showing a more practical implementation of the **kwargs syntax.

def summr(**kwargs):
    total = 0
    print(kwargs)
    print(f"Dict keys: {kwargs.keys()}")
    print(f"Dict values: {kwargs.values()}")
    
    for kwarg in kwargs.values():
        total += kwarg
    print(f"The total is: {total}.")
    return total

Wecan use this function to now add three numbers together, remembering that we must provide parameter names with our parameter values.

>>> summr(first=1,second=2,third=3)

""" The result is printed below. """
{'first': 1, 'second': 2, 'third': 3}
"Dict keys: dict_keys(['first', 'second', 'third'])"
"Dict values: dict_values([1, 2, 3])"

"The total is: 6."

  • Simply printing kwargs in the function shows that a dictionary has been created from the supplied keyword arguments.
  • We can access the keys using the .keys() method.
  • We can access the values using the .values() method.

A More Practical Example

Optional keyword arguments **kwargs are often used to provide some added functionality to a function. Remember that these keywords are optional and so the function must be written in such a way that no error is raised if there are no **kwargs added to the function call.

In our next example we'll write a function to calculate the area of a circle given the radius. We'll make an allowance for optional keyword arguments, and set the function up so that if a named parameter printbool=True is passed to the function then the function will print the area result out as a string.

The radius is a required argument and so must be defined before the optional **kwargs.

import math

def circlearea(r,**kwargs):
    area = round(math.pi*r**2,3)
    if 'printbool' in kwargs.keys():
        if kwargs['printbool'] == True:
            print(f"The area of a circle of radius {r} is {area}.")    
    return area

The circlearea function will run with or without **kwargs, but if printbool=True is added to the function call then the area of the circle will be printed out.

>>> c1 = circlearea(15) # no print of the area

>>> c2 = circlearea(15,printbool=True)
"The area of a circle of radius 15 is 706.858."

Try and write your own version of circlearea where you can set the number of decimal points to round the area result to as an optional keyword argument.

What's in a Name?

Up to this point have made use of the convention *args and **kwargs when referring to optional arguments and keyword arguments respectively, but there is nothing stopping you naming these arguments whatever you like. It is the unpacking operator (* or **) not the name which designates the arguments as optional. 

def sumof(*numbers):
    total = 0
    for number in numbers:
        total += number
    print(f"The total is: {total}.")
    return total

Order is Important

The order in which you define your required arguments, your optional arguments, and your optional keyword arguments in a function is very important. The order is easy to remember as required arguments are always first and optional keyword arguments are last (*args comes before **kwargs).

  1. Required Arguments.
  2. Optional Arguments (*args).
  3. Optional Keyword Arguments (**kwargs).

Further Use of Unpacking Operators

The single and double asterisk that you've been using to define optional arguments and optional keyword arguments are known as unpacking operators and don't only have to be used within the context of a function. Provided you are working with Python 3.5 or later (see PEP 448) then you can use these unpacking operators on any iterable (*) or dictionary (**).

Let's unpack a few examples (see what I did there).

Unpacking Iterables

Any iterable (lists, tuples, sets) can be unpacked using the single asterisk (*) syntax. By unpacking, we mean that each item in the iterable is considered separately as opposed to the iterable being considered as a whole. This has some useful benefits as we'll demonstrate below.

 Merging Lists

You can use unpacking to quickly merge two lists (or tuples) into a single list.

# Unpacking an iterable with the * operator
list1 = [1,2,4]
list2 = [5,6,7]

list3 = [*list1,*list2]
print(list3)
[1, 2, 4, 5, 6, 7]

If you neglect to use the unpacking operator then you'll end up with a new list containing the two lists nested inside.

>>> list3_nested = [list1,list2]
>>> print(list3_nested)
[[1, 2, 4], [5, 6, 7]]

Merging Dictionaries

The unpacking operator (**) is very useful if you need to merge two dictionaries.

# merging dictionaries with the ** operator
dict1 = {'a':1,'b':2,'c':3}
dict2 = {'d':4,'e':5,'f':6}
merged_dict = {**dict1,**dict2}
print(merged_dict)
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}

PEP 448 makes it clear that when working with dictionaries (remember keys must be unique), that values called later will always override earlier ones.

# overwriting a dictionary key when using **operator
dict3 = {'bob':23,'john':43,'sally':21}
dict4 = {'rob':25,'john':25}
merged_dict = {**dict3,**dict4}

In this example the key "john" is called twice and so the value associated with the dictionary called second (dict4) overwrites the earlier value associated with "john" (dict3).

print(merged_dict)
{'bob': 23, 'john': 25, 'sally': 21, 'rob': 25}

Wrapping Up

We've come to the end of this tutorial on optional arguments and keyword arguments in Python functions. Hopefully this has helped to demystify what is actually quite a simple concept, but often looks rather intimidating to newer Python developers. 

To summarise:

  • We use the single asterisk unpacking operator *args to add optional arguments to a function or when we wish to vary the number of input parameters from one implementation of a function to the next.
  • We use the double asterisk unpacking operator **kwargs to unpack optional keyword arguments in a function.
  • Optional keyword arguments are treated within a function as a dictionary where the parameter name forms the key and the parameter value forms the value in a key:value pair.
  • The order in which arguments are listed in a function is important: optional arguments are added after required arguments and optional keyword arguments are added after optional arguments.
def function_name(required1,required2,*args,**kwargs):
  • Provided that you are working with Python 3.5+, you can use the unpacking operators * and ** to unpack iterables and dictionaries respectively. This is very useful (among other things) if you wish to merge lists, tuples, or dictionaries.

Thanks for reading this turorial. Now that you have some insight into the use of unpacking operators and how they specifically apply to optional arguments and keyword arguments in Python functions you should use this new found knowledge to create something great.

Share this
Comments
Canard Analytics Founder. Python development, data nerd, aerospace engineering and general aviation.
Profile picture of andreww
Share Article

Looking for a partner on your next project?

Contact Us