Professional Documents
Culture Documents
When I started learning Python, I was very confused regarding what args, kwargs, * and ** does. And I feel there
are few like me who had this confusion and problem. With this post, I intend to reduce (hopefully I can eliminate)
that confusion.
Throughout this post, I will be using ipython and I suggest you to try everything on ipython as well. We will
intentionally make some mistakes along the way, so that we can understand this topic better.
Let's divide our work under five sections:
1.
Understanding what '*' does from inside a function call.
2.
Understanding what '*args' mean inside a function definition.
3.
Understanding what '**' does from inside a function call.g
4.
Understanding what '**kwargs' mean inside a function definition.
5.
A practical example of where we use 'args', 'kwargs' and why we use it.
Understanding what * does from inside a function call.
Let's define a function "fun" which takes three positional arguments.
In [5]: def fun(a, b, c):
...: print a, b, c
...:
...:
Call this function passing three positional arguments
In [7]: fun(1,2,3)
123
#Output
So, calling this function by passing three positional arguments, prints the three arguments passed to the function.
Let's make a list with three integer values in it.
In [8]: l = [1,2,3]
Let's use '*' now.
In [9]: fun(*l)
123
#Output
In [11]: fun(*l)
579
#Output
Let's make some errors now. Let's put four values in "l".
In [12]: l=[3,5,6,9]
Now, try to call function "fun".
In [13]: fun(*l)
--------------------------------------------------------------------------TypeError
Traceback (most recent call last)
/home/akshar/branding_git/netconference/<ipython console> in <module>()
TypeError: fun() takes exactly 3 arguments (4 given)
So, in our last statement which is 'fun(*l)' we did not get a proper output and a TypeError was raised. See the error,
it says "TypeError: fun() takes exactly 3 arguments (4 given)".
Why did it happen?
list 'l' contains four values. So, when we tried 'fun(*l)', 'l' was unpacked so that its value could be sent as positional
arguments. But, "l" has four values in it. So, writing 'fun(*l)' was equivalent to writing 'fun(3,5,6,9)'. But, 'fun' is
defined to take only three positional arguments and hence we got this error. Similary, you can follow same steps
with two values in list 'l' and notice the error.
In [14]: l=[5,6]
In [15]: fun(*l)
--------------------------------------------------------------------------TypeError
Traceback (most recent call last)
/home/akshar/branding_git/netconference/<ipython console> in <module>()
TypeError: fun() takes exactly 3 arguments (2 given)
Let's mix '*l' with a positional argument.
In [16]: fun(1, *l)
156
#Output.
Here we gave one positional argument which is 1 and two values i.e 5 and 6 were unpacked from "l" and hence 1,5
and 6 were passed to 'fun'.
Hope you were able to follow what '*' does when used inside a function call.
Understanding what '*args' mean inside a function definition.
Let's change the function definition now.
#Output
So, we can see that 'a' is assigned 1 which was the first positional argument. There is only one parameter "*args"
defined after "a". So, "args" received a tuple containing the positional arguments beyond the formal parameter list.
So, args received 2, 3 and 4 as a tuple.
We can also call "fun" with just one positional argument. Let's do that.
In [23]: fun(1)
a is 1
args is ()
#Output
#args received an empty tuple
Here, we passed only one argument to the function which was assigned to the formal parameter 'a'. So, 'args'
received an empty tuple as can be seen from the output.
After we have "args", we can extract the values and do whatever we want. Let's redefine "fun".
In [24]: def fun(a, *args):
....: print a
....: print "args can receive a tuple of any number of arguments. Let's print all that."
....: for arg in args:
....:
print arg
....:
....:
We can call "fun" with any number of arguments.
In [25]: fun(1,5,6,7)
1
#Output
args can receive a tuple of any number of arguments. Let's print all that.
5
6
7
Since 'args' is a tuple, we could iterate over it.
Now, let's consider a case where we use whatever we saw till here. Here we need to use two functions. First
function has to take an arbitrary number of arguments and it has to calculate the sum of all the arguments except
the first argument. Also, this function has to make use of another function to calculate the sum. Weird use case, but
we just need to recap whatever we did till here. The objective here is to see how we get a variable number of
arguments in a function and pass these arguments to another function.
Let's first write the function which has to calculate the sum i.e the second function. For our use case, this function
will be used in the first function which we are yet to write.
In [26]: def calculate_sum(*args):
....: return sum(args)
....:
Here we make use of 'sum'. Function 'sum' is an inbuilt function which takes a tuple or a list and return the sum of
all the elements in the tuple. From our function definition we can see that 'args' will receive a tuple containing all the
positional arguments passed to this function. So, 'args' will be a tuple and can be directly used as an argument to
function 'sum'. Let's write the other function which takes any number of arguments and uses previous function to
calulate the sum of all arguments except the first argument.
In [29]: def ignore_firstargs_calculate_sum(a, *iargs):
....: required_sum = calculate_sum(*iargs)
....: print "sum is", required_sum
....:
....:
We can pass any number of arguments to this function. First argument will be recieved by 'a' which is a formal
parameter. All other arguments will be recieved by 'iargs' as a tuple. As per the case we are considering, we want to
calculate the sum of all arguments except the first. So, we leave 'a' as it receives the first argument. 'iargs' is the
tuple containing all arguments except the first. We will make use of function 'calculate_sum'. But 'calculate_sum'
expects number of positional arguments to be sent to it which it will receive in 'args' as a tuple. So, in function
'ignore_firstargs_calculate_sum' we need to unpack 'iargs', as it is a tuple, and then send the unpacked positional
arguments to 'calculate_sum'. Remember, we used '*' to unpack a list/tuple.
So, we write 'required_sum=calculate_sum(*iargs)'.
We can't write 'required_sum=calculate_sum(iargs)' because we need to unpack the values in the tuple 'iargs'
before sending to 'calculate_sum'. Not using '*' will not unpack the values and hence we won't have the desired
behaviour. Let's use the function we wrote.
In [34]: ignore_firstargs_calculate_sum(3,1,2,6)
sum is 9
#Output
The output is the sum of all arguments except the first argument.
Understanding what '**' does when used from inside a function.
Let's consider a simple example first. Let's define a function which takes three arguments.
In [35]: def fun(a, b, c):
....: print a, b, c
....:
....:
Let's call this function in various ways.
In [36]: fun(1,2,3)
123
#Output
In [43]: d={'a':7,'b':8,'c':9}
In [44]: fun(**d)
789
#Output
...:
...:
...:
...:
...:
...:
...:
...:
...:
...:
...:
self.name = name
def save(self, force_update=False, force_insert=False):
if force_update and force_insert:
raise ValueError("Cannot perform both operations")
if force_update:
#Update an existing record
print "Updated an existing record"
if force_insert:
#Create a new record
print "Created a new record"
We defined a class. We can create objects of this class and objects of this class have a method "save()". Assume
that the objects of this class can be saved in a database which is being done inside the save() method. Depending
on the arguments we pass to save() method, it is determined whether new records need to be created in the
database or an existing record need to be updated.
We want a new class where we want 'Model' behaviour but we only want to have save the objects of this class after
we have checked some conditions. So let's subclass 'Model' and override 'save()' of 'Model'.
In [6]: class ChildModel(Model):
...: def save(self, *args, **kwargs):
...:
if self.name=='abcd':
...:
super(ChildModel, self).save(*args, **kwargs)
...:
else:
...:
return None
...:
Actual saving of object(as per our assumption, connecting with database and creating/updating) happens in the
"save" method of "Model". So we need to call the "save()" method of superclass from save() method of ChildModel.
Also, save() method of subclass i.e ChildModel should be able to accept any arguments that save() of superclass
accepts and must pass through these arguments to the superclass save(). So we have "*args" and "**kwargs" in
the argument list of subclass save() method to receive any positional arguments or keyword arguments beyond the
formal parameter list.
Let's create an instance of ChildModel and save it.
In [7]: c=ChildModel('abcd')
So, we created an instance of ChildModel with name='abcd'.
In [9]: c.save(force_insert=True)
Created a new record
#Output
Here, we passed a keyword argument to save() of the object. The save() we called is the subclass save(). It
received a dictionary containing the keyword argument in "kwargs". Then it used "**" to unpack this dictionary as
keyword arguments and then passed it to the superclass save(). So, superclass save() got a keyword argument
'force_insert' and acted accordingly.
10
Ever wondered what the *args and **kwargs that you keep seeing in Python functions mean? I certainly did. So I
did some searching and it turns they're pretty nifty, not to mention useful.
That * and the ** operators both perform two different, but complementary operations depending on where they're
used. When used in a method definition, like so:
They perform an operation called 'packing'. True to it's name, what this does is pack all the arguments that this
method call receives into one single variable, a tuple called args. You can use any variable name you want, of
course, but args seems to be the most common and Pythonic way of doing things.
Once you have this 'packed' variable, you can do things with it that you would with a normal
tuple.args[0] and args[1] would give you the first and second argument, respectively. If you convert the args tuple to
a list you can also modify, delete and re-arrange items in it.
So how do you pass these packed arguments to another method? Here's where unpacking comes in to play:
So there's the same * operator again, but this time it's in the context of a method call. What it does now is explode
the args array and call the method as if you'd typed in each variable separately.
Here's another example that might make things a little more clear:
11
# > Hello
# > awesome
# > world!
This happens simply because we're changing the first two arguments before passing them off to func1.
The normal rules governing method definition apply here calling func2('a', 'b', 'c', 'd') will raise an error because it
will in turn call func1 with four arguments, which it doesn't expect.
The same principle applies to **kwargs too, except that in this case it applies to keyword arguments,
andkwargs turns out to be a dict.
Combined together packing and unpacking lets you do a lot of things like:
I'm sure there's a lot more uses you can think of and see for these little *s. Have fun.
Edits & Credits:
Thanks to Florian Mayer for pointing out that super(self.__class__, self).__init__() would result in infinite
recursion if a subclass is written and used. self would still refer to the instance of the subclass when the call
If you enjoyed this article, do tweet it or share the link with others.
You could also look at other articles with the same tag: python
Brought to you by the folks behind Runway7, a collection of useful APIs that make web development faster and
more convenient.
12