*args
and **kwargs
This lesson will explain what *args
and **kwargs
are in Python.
1. *args
Let's consider the following function.
def mul(x, y):
return x * y
This is a simple enough function that takes in two numbers. However, what if you would like a function that can multiply any number of numbers. e.g. 3 * 4 * 5 * 6
One way of doing this is with a list.
def mul(num_list):
total = 1
for num in num_list:
total *= num # multiply total by num
return total
list_of_nums = [3,4,5,6]
print(mul(list_of_nums)) # prints 360
This though requires that we create and pass a list of numbers, we can do it another way using *args
.
def mul(*args):
total = 1
for num in args:
total *= num
return total
print(mul(3,4,5)) # prints 60
print(mul(3,4,5,6)) # prints 360
Here we use *args
to represent any number of positional arguments. These get packed into what is known as an iterable (in this case it is a tuple) which we can then loop over. The *
operator in front of args is known as the unpacking operator.
Note that args
is just a name, we could call this something else like numbers
.
For example:
def mul(*numbers):
total = 1
for num in numbers:
total *= num
return total
print(mul(3,4,5)) # prints 60
print(mul(3,4,5,6)) # prints 360
However, it is convention to use args
, other programmers will understand better if you stick to this convention!
Here is another example that finds the maximum from a number of numbers.
def maximum(*args):
# set current max to first item in args
current_max = args[0]
# iterate over the rest of args
for x in args[1:]:
if x > current_max:
current_max = x
return current_max
print(max(1,4,2,7,4)) # prints 7
print(max(1, 4)) # prints 4
2. **kwargs
**kwargs
is very similar to *args
but instead of being positional arguments, it takes in keyword (named) arguments.
**kwargs
will be stored in a dictionary. So you can iterate over the keys()
, values()
or both using items()
.
In the example below we iterate over the values. In this case the price of each item.
def bill(**kwargs):
total = 0
for price in kwargs.values():
total += price
return total
print(bill(bread=5, milk=1.5, orange_juice=1)) # prints
print(bill(banana=3, pasta=3))
print(bill(bread=5, pasta=3, orange_juice=1, milk=1.5))
In the next example we iterate over both the item and the price using the items()
method. Note we have renamed the keyword arguments in the function as bill_items
. Again we can call it what we want, we just need to put the **
unpacking operator in front.
def bill(**bill_items):
total = 0
for key, value in bill_items.items():
print(f"Adding {key} at price {value}")
total += value
return total
print(bill(bread=5, milk=1.5, orange_juic=1))
print(bill(banana=3, pasta=3))
print(bill(bread=5, pasta=3, orange_juice=1, milk=1.5))
# prints ....
# Adding bread at price 5
# Adding milk at price 1.5
# Adding orange_juic at price 1
# 7.5
# Adding banana at price 3
# Adding pasta at price 3
# 6
# Adding bread at price 5
# Adding pasta at price 3
# Adding orange_juice at price 1
# Adding milk at price 1.5
# 10.5
Again, it is convention to use kwargs
, so stick to that.
3. Combining *args and **kwargs
We won't labour on this point, but you can use both in a function, but you need to get the order correct.
Normally we have that positional arguments come before keyword arguments. e.g.
def my_func(x,y,z=10):
pass
For *args
and **kwargs**
, *args
must come after any positional arguments and **kwargs
before any keyword arguments.
However, I would just stick to this order which is common practice:
- Positional Arguments
- Keyword Arguments
*args
arguments**kwargs
arguments
Here are some example function definitions that work and don't work. Try copying them into main.py and running.
3.1 Correct Usage
# This is fine
def my_func(x, y, *args, **kwargs):
pass
# This is fine
def my_func(x, y, *args):
pass
# This is fine
def my_func(x, y, **kwargs):
pass
# This is fine, we now include a keyword argument z
def my_func(x, y, z=10 *args, **kwargs):
pass
3.2 Incorrect Usage
# This is NOT fine, *args before **kwargs
def my_func(x, y, **kwargs, *args):
pass
# This is NOT fine, *args before **kwargs
def my_func(x, y, z=10, **kwargs, *args):
pass
# This is NOT fine, positional arguments come before *args and **kwargs
def my_func(*args, **kwargs, x, y)):
pass
# This is NOT fine, keyword arguments come before *args and **kwargs
def my_func(x, y, *args, **kwargs, z=10):
pass
=== TASK ===
Create a function called initials that takes in a persons names and then returns the initials. You should pass the names using *args
.
For example for James Marshall Hendrix
it should return J.M.H
.
Or, for John Ronald Reuel Tolkien it should return J.R.R.T
(one *args
to rule them all).
Copy this code into a new Python file to get started.
def initials(*args):
pass
if __name__ == "__main__":
print(initials("James", "Marshall", "Hendrix")) # should print the return value of "J.M.H"
print(initials("John", "Ronald", "Reuel", "Tolkien")) # should print the return value of "J.R.R.T"