*args and **kwargs explained.

If you have ever felt confused when you came across code that contains *args and **kwargs as function parameters, you’re not alone. This syntax is used specify that a function can be called with any number of arguments and It can be very confusing at first. In this article, I explain what *args and **kwargs mean and how they can be used in function calls.

Firstly, let’s talk about functions and tuples. In Python, a function is a block of related statements that perform a particular task. Functions are defined using the def keyword. A function must have a name and can have any number of parameters. Let’s create our own function to find the sum of three numbers:

>>> def sumall(arg1, arg2, arg3):
...     result = arg1 + arg2 + arg3
...     return result

>>> sumall(10,10,10)
30

This is a simple function that takes three numbers as arguments, finds the sum of the three and returns the result. Arguments that are passed in this way are referred to as positional arguments. The problem with this function is that it can only find the sum of three numbers. If we pass in less than three arguments or more than three arguments to it, we get an error:

>>> sumall(10, 20)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    sumall(10, 20)
TypeError: sumall() takes exactly 3 arguments (2 given)

>>> sumall(10, 20, 9, 5)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    sumall(10, 20, 9, 5)
TypeError: sumall() takes exactly 3 arguments (4 given)

For the sumall() function to be useful, it should be able to take a variable number of arguments as input. To do this, we have to first gather the arguments into a tuple. A tuple in Python consists of a number of values, separated by commas, for instance:

>>> tp = 4, 10, 67
>>> type(tp)
<type 'tuple'>
>>> tp
(4, 10, 67)

As you can see, tuples can be created without enclosing parentheses. The Python interpreter will always enclose tuples in parentheses on output so that nested tuples are interpreted correctly.

Functions in Python can take a variable number of arguments. A parameter name that begins with a * gathers or packs elements into a tuple. This special argument can have any name you want to give it, but args is conventional. Let’s re-write the sumall() function to use *args.

>>> def sumall(*args):
...     print(args)

>>> sumall(10, 20, 9, 5)
(10, 20, 9, 5)

When the sumall() function is called the *args variable collects all the arguments passed to the function and packs them into a tuple. This makes it possible to consume the entire tuple and do something useful with it.

>>> def sumall(*args):
...     result = 0
...     for num in args:
...         result += num
...     return result
... 

>>> sumall(1)
1
>>> sumall(1, 25)
26
>>> sumall(1, 25, 30)
56
>>> sumall(1, 20, 30, 10)
61


The sumall()function now takes any number of arguments without throwing errors thanks to the *args special parameter. This feature is great because it allows you to build the data up before the function call:

>>> data = (10, 20, 30, 40)
>>> sumall(*data)
100

Here, adding the * to the data argument when the function was called caused the data tuple to be scattered. This works for any sequence that you want to pass to a function that takes multiple arguments. For example, the divmod() builtin function takes exactly two arguments and returns a tuple, a quotient and a remainder. It doesn’t work with a tuple or list.

>>> divmod(10,2)
(5, 0)

>>> numbers = (10, 2)
>>> divmod(numbers)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    divmod(numbers)
TypeError: divmod expected 2 arguments, got 1


Unpacking or scattering the tuple works:

>>> divmod(*numbers)
(5, 0)

Using **kwargs

**kwargs works in a similar fashion to *args.**kwargs Allows you to pass any number of keyword arguments to a function. The double asterisk ** tells Python to pack arguments that follow into a dictionary that maps keywords to values. The keyword can be called anything you want, but kwargs is a common choice.


>>> def mixed_args(*args, **kwargs):
...     print(args)
...     for name, value in kwargs.items():
...         print('{0} = {1}'.format(name, value))
...         
...     
... 
>>> mixed_args(1,2,3,4,5,6, name='Vuyisile', surname='Ndlovu')
(1, 2, 3, 4, 5, 6)
surname = Ndlovu
name = Vuyisile
>>> mixed_args(1, name='Vuyisile', surname='Ndlovu', occupation='Developer')
(1,)
surname = Ndlovu
name = Vuyisile
occupation = Developer

Ordering the arguments

When writing functions that use *args and **kwargs in addition to normal arguments, the following order should be maintained:
some_func(arg0, kwarg0=val0, *args, **kwargs) Positional arguments come first, then keyword arguments. After the formal arguments, you can then pass in the *args and **kwargs arguments.

Conclusion

In this article you learned how to create functions that take a variable number of positional and keyword arguments using *args and **kwargs. The article also touched on how tuples work and how they can be passed as arguments to functions.

 

Vuyisile

 

2 thoughts on “*args and **kwargs explained.

Leave a Reply

%d bloggers like this: