Default Arguments Caught Up
Here’s the concept that blow your mind
Imagine writing code that’s both flexible and efficient without the usual clutter of endless parameters. That’s the magic of default arguments in Python. This feature lets you set pre-defined values in your functions, creating a streamlined, adaptable codebase. Whether you are building simple scripts or complex applications, understanding default arguments can make your functions smarter and your code cleaner. Let’s dive into this powerful concept and see how it can simplify your python projects like never before.
Table of Content
- Default Arguments
- Default arguments that could change over time
- Positional and Keyword Arguments
- Unpacking Arguments
- Variable number of positional arguments
Default Arguments
If there is some argument value that would be used most of the times while
calling the function, we can make it a default value for the parameter. This
default value will automatically be used as the argument, if the user of the
function does not provide the corresponding argument for that parameter.
These default values are called default arguments. These values are provided
in the header of the function definition, let us see the syntax of specifying a
default argument with the help of a simple example.
def func(a, b=5):
print(a, b)
In this function definition we have two parameters, a and b. For the
parameter b, we have assigned a default argument value 5. The default
argument value is placed after the parameter name with the equal to sign (=) in between. Now, we can call this function with either two arguments or one
argument.
func(10, 2)
func(10)
In the call func(10,2) parameter a is assigned value 10 and b is
assigned value 2. In the call func(10), a is assigned value 10; there is no
argument value for parameter b so the default argument value is used and b
is assigned value 5.
So, if a parameter is given a default value, then providing argument for it
becomes optional. If you provide the argument, then its value will be used otherwise the default value will be used. In this way you can make some
parameters optional in a function.
def simple_interest(principal, time, rate=5):
return (principal * rate * time) / 100
s1 = simple_interest(1000, 4, 7)
s2 = simple_interest(1000, 4)
In this function, we are calculating and returning simple interest. We have
three parameters from which rate is an optional parameter because we
have provided a default argument for it. The other two are the required
parameters, meaning we have to provide arguments for these parameters
during the function call. We have written two function calls; in the first one
we have supplied a value for rate. In the second call, we have not supplied
any value for rate, so 5 will be used.
We specify default arguments when there are certain parameters in a
function for which a common value is used most of the times. For example,
in our simple_interest function, the value of rate is 5 in most of the
calls and so we specified it as the default argument. Thus, the argument
value that occurs frequently is specified as the default argument. There is no
need of writing the argument on each invocation. By providing default arguments, we can provide a general functionality to
the user and if he wants some special behaviour, he can supply his own
arguments. More than one parameter can have default arguments, for example in our
simple_interest function, we can provide a default argument for
parameter time also.
def simple_interest(principal, time=2, rate=5):
return (principal * rate * time) / 100
s1 = simple_interest(1000, 4, 7)
s2 = simple_interest(1000, 4)
s3 = simple_interest(1000)
Now we can call the function with three, two or only one argument. In the
last call, we have provided the argument only for principal, and the
default values for time and rate will be used.
If a parameter has a default argument then all the parameters following it
should also have default arguments. Therefore, parameters that have to be
given default value should be placed at the last.
The definition is wrong because we have a parameter with a
default value and after that we have a parameter without default value.
def simple_interest(principal, time=2, rate): # wrong
return (principal * rate * time) / 100
Similarly, this definition is also wrong.
def simple_interest(time=2, rate=5, principal): #wrong
return (principal * rate * time) / 100
All optional parameters must come after the required parameters.
Let us see with the help of some example how the default arguments make
our function more flexible and versatile.
def display_line():
print('-' * 30)
display_line()
We have a simple function that displays a line on the screen. When this
function is called we get a line of 30 dashes. This function can be made
more flexible by providing optional parameters.
def display_line(character='-'):
print(character * 30)
We introduced a parameter named character and provided a default
value for this parameter. Inside the definition, instead of '-' we write the
parameter name. Now by using this function, we can draw a line of any
character.
display_line('%')
display_line('*')
display_line()
We can still call it without any argument and in that case, it prints a line of
dashes. Now the user can draw a line made up of any character but the
length of the line is fixed, it is always 30 characters long. We will the make
length also flexible by introducing another optional parameter.
def display_line(character='-', length=30):
print(character * length)
display_line('%', 20)
display_line('*', 50)
display_line('*')
display_line()
If we don’t provide the value of length, the default value of 30 is used.
And if we don’t provide any argument then the default arguments for both
the parameters are used. So now the user can draw a line of his own choice
and if he just writes display_line(), the function will draw a line of
30 dashes.
This is an example of polymorphism, which means one thing many forms. A
single function can be called in different ways. This example also shows
how our functions can evolve with time while remaining backward
compatible.
In our next example we will provide a default argument in the
display_list function that we had written earlier. In this function, most
of the times numbers will be printed in the decimal base so we can make 10 as
the default argument value for base.
numbers = [134, 2567, 366, 521, 689]
def display_list(L, base=10):
if base == 2:
ch = 'b'
elif base == 8:
ch = 'o'
elif base == 16:
ch = 'X'
else:
ch = 'd'
for n in L:
print(f'{n:{ch}}', end=' ')
print()
display_list(numbers)
display_list(numbers, 8)
display_list(numbers, 16)
When the base is not specified, numbers are displayed in decimal.
Sometimes a parameter value is not required or not applicable in some cases.
In these situations, you can make that parameter optional by providing a
default value. For example, in the function, it is optional to
provide a sports grade or an arts grade.
def result(total, sports=None, arts=None):
print('Total marks = ', total)
if sports is not None:
print('Sports Grade', sports)
if arts is not None:
print('Arts Grade', arts)
result(98, 'A')
result(78, 'B', 'C')
result(88)
Default Arguments that could Change over Time
If you use a default argument that could change over time then it could lead
to unexpected behaviour. Let us understand this with the help of some
examples.
d = 6
def func(p = d):
print(p)
func() #default argument used for parameter p
func(80) # 80 used as argument for parameter pd = 100
func() #default argument used for parameter p
d += 20
func() #default argument used for parameter p
We have a small function definition which has an optional parameter. The
variable d is used as the default argument for the parameter p. You would
expect that whenever the function is called without any argument, the
parameter p should be initialized with the value of variable d. This is the
output that you would expect from this code.
6 80 100 120
The first call has no argument and so the parameter p should be initialized
with the value of d, thus p should be 6. In the second call, argument is
provided, so p is 80. In the third call, default argument will be used so p
should be 100, because d is now 100. Similarly in the last call you expect p
to be 120, because d is now 120. This is the behaviour that you expect from
this program but when you run it, it gives this output.
6 80 6 6
The reason for this unexpected output is that the default argument gets
evaluated only once when the function definition (def statement) executes.
That same object is bound to the parameter each time the function needs to
use default argument. The default argument is not re-evaluated each time the
function is called function call is executed.
In our example program, when the def statement executed, the parameter p
was bound to integer object 6 because the variable d was referring to it. This
object was fixed as the default argument for this parameter and so was used
in all those calls that did not supply the corresponding argument. The
variable d was bound to other objects over time but p was always bound to
the original object.
Let us take one more example to see how we can write problematic
functions if we are not aware of this issue. Suppose we write a simple
function to log some information along with the time when the information
is logged. It has two arguments and we have provided default value for both
of them.
from time import sleep
from datetime import datetime
def log(information='Everything Ok…', time=datetime.now()):
print(information, time)
log('Some problem...', '16:59:49')
log()
sleep(2)
log('Another Problem…')
sleep(3)
log()
We want the caller of the function to specify information and time. If
no argument is provided for the parameter information then the string
‘Everything Ok…’ will be used and if value for parameter time is not
provided then value returned by the method datetime.now will be used.
This is the sort of output that we expect from this code.
Some problem... 16:59:49
Everything Ok… 2023-06-29 12:15:03.830804
Another Problem… 2023-06-29 12:15:05.833038
Everything Ok… 2023-06-29 12:15:08.833439
In the first call, we have provided arguments for both parameters, so those
values are used. In the second and fourth calls we have not provided any
argument so the default values will be used for both parameters. In the third
call, default value will be used for the second parameter. We expect the
current date and time returned by the method datetime.now to be used as
the default argument, when the second argument is missing in the call. The
actual output that we get by executing the above code is this.
Some problem... 16:59:49
Everything Ok… 2023-06-29 12:16:02.296959
Another Problem… 2023-06-29 12:16:02.296959
Everything Ok… 2023-06-29 12:16:02.296959
Each time the function is called without the second argument, the same time
is printed. This time that we are getting in all the calls, is actually the time
when the def statement was executed. When the function definition was
executed, the method datetime.now got executed and the object returned
by it was fixed as the default argument for parameter time. This is whey
whenever we don’t provide the second argument, we see the same time
getting printed. This is not the kind of behaviour that we expect from our
function. Similar type of problem shows up when a mutable object is specified as the
default argument and the function modifies the parameter. Let us see this
with the help of a very simple example.
def func(a, L=[]):
L.append(a)
print(L)
func(10, [1, 2, 3])
func(8, [5, 6])
func(9)
func('Hello')
func(100)
We have a function with two parameters, the second one has an empty list as
the default argument. The expectation is that if the second argument is
omitted when this function is called a fresh empty list object will be created
and bound to the parameter name. This is the output that we expect.
[1, 2, 3, 10]
[5, 6, 8]
[9]
['Hello']
[100]
In the first call, 10 should be appended to the list [1, 2, 3] and printed. In the
second call, 8 should be appended to the list [5, 6] and printed. In the last 3
calls, the second argument is not provided so an empty list should be used as
the second argument, and the first argument should be appended to that
empty list. The output is again different from what is expected.
[1, 2, 3, 10]
[5, 6, 8]
[9]
[9, 'Hello']
[9, 'Hello', 100]
When the function definition was executed, an empty list object was created
and the same single list object is being used as the default every time the
function is called. The function is modifying the list object which is sent as
argument. When no argument is sent, the function uses the default argument
and modifies it. The same modified list object is used another time when the
function is called. As a result, the parameter is not initialized with proper
default value each time it is called.
So, we saw three examples where the expected output and the real output
were different. And it was because the default argument that we used could
change over time. Thus, it is a bad idea to use default argument values that
could change over time. When you use a literal of immutable type for example 23,
‘hello’, (2,3,4), as a default argument then there is no problem because the
objects of immutable types cannot be changed. When you specify a literal of mutable type(eg. [1,2], {}) then problems could arise as the mutable object
could be changed inside the function or the function might return it and it
could be changed even outside the function. The point is that setting a
mutable object as the default argument is dangerous as it could change. And so,
there is no guarantee that each subsequent call will get the default initial
value. If we use a variable as a default argument as in our first example
then also we don’t get what we expect as the variable can be re-assigned.
Now we will see what can we do to get the expected output even if we use
something that can change over time as default argument. The common
idiom is to use None as the default and then test for it explicitly inside the
function body. The actual default is assigned to parameter inside the function
body if the parameter is None. Let us see how we can use this trick in the
examples that we have seen.
# old function
def func(a, L=[]):
L.append(a)
print(L)
# modified function
def func(a, L=None):
if L is None:
L = []
L.append(a)
print(L)
We have set the default to None, so parameter L is optional. If a list is
supplied by the caller, it will be used inside the function and if it is not
supplied then L will be None, and inside the function L will refer to a newly
created empty list object. Now every time the function is called a fresh
empty list object is created. The default is not shared between subsequent
calls. So, if you want your parameter to be bound to a fresh mutable object
each time a function is called, you can use this trick. We can use the same trick in the log function.
#old function
def log(information='Everything Ok…', time=datetime.now()):
print(information, time)
#modified function
def log(information='Everything Ok…', time=None):
if time is None:
time = datetime.now()
print(information, time)
In this function if second argument is not provided then time will be None,
the if condition will be True and time will be assigned the return value of
the method datetime.now(). Now if you run your previous code with
this modified version of the function, you will get the expected output; the
time will be different in each function call.
Similarly, we can use this idiom in the first example that we saw.
#old function
def func(p = d):
print(p)
#modified function
def func(p=None):
if p is None:
p = d
print(p, end = ' ')
Positional and Keyword Arguments
In this section, we will see an alternative way of supplying arguments in a
function call. We have seen that the argument values that we supply in the
function call are matched to parameter names by position from left to right.
For example, in this code the first argument is matched to first
parameter, second argument is matched to second parameter and third
argument is matched to third parameter.
def func(name, title, salary):
print(f'{name} is a {title} and gets {salary}')
func('Nick', 'manager', 5000)
The interpreter matches the arguments based on their position. Arguments
matched by their position are called positional arguments. In Python, there is
an alternative way to specify arguments in the function call. Here, is a
function call that uses the alternative way.
func(name='Nick', title='manager', salary=5000)
In this syntax, we write the parameter name then equal sign, and then the
argument value that we want to assign to the parameter. We clearly specify
which value is for which parameter and so now matching of argument and
parameter is explicit. If we specify a name in the call that does not match
any of parameters in the definition then there will be an error.
When we specify parameter names in the call, the arguments are identified
by the parameter name and not by position, so the order of arguments does
not matter. We can write the function call with different orders of
parameter=argument pairs.
func(title='manager', salary=5000, name='Nick')
func(title='manager', name='Nick', salary=5000)
These two calls are equivalent to the previous one and all will give the same
output.
The arguments that are matched by position are called positional arguments
and the arguments that are matched by parameter name are called keyword
arguments or named arguments. Thus, a positional argument is an
argument that is assigned to a parameter based on its position in the
argument list while a keyword argument is assigned to a parameter based on
the parameter name specified along with the argument.
You can mix positional and keyword arguments in a single call. When you
do this, all the positional arguments have to appear before the keyword
arguments.
func('Nick', salary=5000, title='manager')
func('Nick', 'manager', salary=5000)
This call will give error as we have used the keyword argument
before the positional arguments.
func(name='Nick', 'manager', 5000)
Both positional and keyword arguments can be used for overriding default
values. Let us modify our function definition so that it has some parameters
with default values.
def func(name, title='developer', salary=3000):
print(f'{name} is a {title} and gets {salary}')
In this definition, name is a required parameter while title and salary
are optional parameters. If you decide to override the default value and
supply your own value, you can use any type of argument, positional or
keyword argument. First let us use positional arguments.
func('Mark')
func('Mark', 'programmer')
func('Mark', 'programmer', 4000)
Output
Mark is a developer and gets 3000
Mark is a programmer and gets 3000
Mark is a programmer and gets 4000
In the first call we supplied one positional argument so default values for
both title and salary are used. In the second call, default value for only salary
is used and in the third call, both default values are overridden.
Now we will write the equivalent calls using the keyword arguments.
func(name='Mark')
func(name='Mark', title='programmer')
func(name='Mark', title='programmer', salary=4000)
In the first call, both default values are used in the second call, only one
default value is used and in the third one, none of the default value is used.
Now that we have learned how to provide keyword arguments, a question
arises, What are the benefits of using these keyword arguments instead of always sending arguments by position. Let us explore some advantages of
using keyword arguments.
In some places, using explicit parameter names in the call makes the code
easier to read.
volume_cylinder(10, 12)
calc_interest(20000, 5, 6):
volume_cylinder(radius=10, height=12)
calc_interest(20000, time=5, rate=6):
We have two function calls with positional arguments and two equivalent
calls that use keyword arguments. In the first call volume_cylinder(10,12), it is not clear which argument is for radius and
which is for height. In the call volume_cylinder(radius=10,
height=12), there is no confusion, the meaning of arguments is obvious.
Similarly, the two calls to the function calc_interest will perform the
same work, but for someone reading the code, the second one is clearer. So,
keyword arguments provide a way to improve readability of the code,
wherever it is important, especially in larger programs. The code becomes
self-documenting, provided that the parameter names are meaningful. Single
letter names like x, y, a will not help in making the call informative.
Get A.I Hub’s stories in your inbox
Join Medium for free to get updates from this writer.
Another benefit of using keyword arguments is that the caller has the
flexibility to specify the arguments in any desired order. With positional
arguments the caller needs to remember the meaning of each position but
with keyword arguments you don’t have to worry about the order in which
the arguments have to be supplied. It is not necessary to specify the
arguments in the same order in which they are specified inside the function
definition. For example, you can call your calc_interest function in
any of the these two ways.
calc_interest(20000, time=5, rate=6)
calc_interest(20000, rate=6, time=5)
To understand the third advantage let us consider this function
again.
def func(name, title='developer', salary=3000):
print(f'{name} is a {title} and gets {salary}')
In this function definition, we have default arguments for the last two
parameters. Now suppose when you call this function, you want to supply
your own value for salary but want to use the default value for title.
With positional arguments it is not possible; there is no way to do this. You
can’t write your call like this.
func('Nick', 6000)
If you do this, you get this output.
Yashvaant is a 6000 and gets 3000
Argument 6000 is assigned to title and default value of salary is used.
So, if you are using positional arguments, you have to write your call like
this.
func('Yashvaant', 'developer', 6000)
Even though you want to use the default value for title, you have to
specify it because you have to override the default value for salary. If you
use keyword arguments you can write your call like this.
func('Yashvaant', salary=6000)
Now there is no need to specify the value for title parameter.
Let us look at one more example that will help us gain a clearer
understanding of this point.
def func(a, b, c=2, d=5, e=8, f=True, g=False):
pass
In this function, the last 5 parameters have default values, so they are
optional. When we call the function, we want to supply our own values for
parameters d and g and use the defaults for the rest of the optional
parameters. With positional arguments, we are forced to write our call like
this.
func(12, 24, 2, 500, 8, True, True)
We have to specify all the arguments, even though parameters c, e and f
need just the default value. When you are using positional arguments and you want to override a default value, you have to override all defaults before
that. But if you use keyword arguments, you can skip over parameters with
defaults. We can write this call using keyword arguments like this.
func(12, 24, d=500, g=True)
Now we can skip the parameters c, e and f and specify only those
parameters for which we want to override default value.
So, the third advantage of using keyword arguments is that you can bind
some optional parameters to specific values and let other parameters take
default values.
def volume_cylinder(radius, height):
return 3.14 * radius * radius * height
print(volume_cylinder(5, 20))
print(volume_cylinder(radius=5, height=20))
print(volume_cylinder(height=20, radius=5))
We have this function that takes in radius and height of a cylinder and
returns its volume. First, we have called it with positional arguments, then
we have called it with keyword arguments. We can reverse the order of the
arguments when we use keyword arguments.
Now let us take a function that has default arguments.
def display_line(character='-', length=30):
print(character * length)
We have seen this function before. If we want to use default for
character and specify our own argument for length we can’t do that
using positional arguments. If we try to do that, we get incorrect results.
display_line(40)
Output
1200
We wanted to display a line of 40 dashes, but we got 1200 as result. It is
because 40 was assigned to the character and the default value of length was used. So, inside the function 40 * 30 was printed. We have to specify the
dash in the call.
display_line('-', 40)
If we use keyword argument, then there is no need to specify the first
argument.
display_line(length=40)
Now default is used for character and we have provided our own value
for length.
numbers = [12, -1, 3, 6, 8, 9, 38, -3, 34, -4]
def display(L, negative=True, odd=True):
for n in L:
if n < 0 and negative == False:
continue
if n % 2 != 0 and odd == False:
continue
print(n, end=' ')
print()
This function displays the numbers of a list L. The last two arguments decide
whether negative numbers and odd numbers will be displayed. If the second
argument is True, negative numbers will be displayed, if it is False negative
numbers will not be displayed. If the third argument is True, odd numbers
will be displayed and if it is False odd numbers will not be displayed. The
default value for both is True.
Inside the function we have used continue statement to skip negative and
odd numbers if the corresponding variables are False. Now let us write some
calls to this function.
display(numbers)
display(numbers, False)
display(numbers, False, True)
display(numbers, True, False)
In the first call we have not supplied the last two arguments, so their default
value will be used. Both negative and odd are True, so all the numbers
will be displayed. In the second call, negative numbers will not be displayed.
The third one is equivalent to second one, negative numbers will not be
displayed and odd numbers will be displayed. In the last call, negative
numbers will be displayed, but odd numbers will not be displayed. By
looking at these calls, we can’t tell which parameter is True and which one is
False and so it is not clear which type of numbers we want to display in a
particular call. We can make the code more readable by using keyword
arguments. Let us write some calls with keyword arguments.
display(numbers, odd=False)
display(numbers, negative=False)
display(numbers, negative=False, odd=False)
These calls are clearer than the previous calls. So, the keyword arguments
make the calls more readable, particularly in the case of Boolean arguments.
The keyword arguments are mostly useful when the functions become
complex and have many parameters and when most of them are optional
parameters.
Keyword arguments enable you to add new parameters to a function, while
remaining backward compatible with existing callers. This is particularly
important when the function accepts variable number of arguments.
Unpacking Arguments
Now, we will see how to use a container instead of individual
argument values in a function call.
def result(m1, m2, m3, m4):
total = m1 + m2 + m3 + m4
per = total / 4
print(f'Total Marks = {total}, percentage={per:.2f}%')
print('Pass' if per > 40 else 'Fail')
result(56, 89, 77, 67)
We have a function that takes marks in 4 subjects calculates total marks and
percentage, prints them and then prints Pass or Fail depending on the
percentage. We have assumed that maximum marks in each subject is 100,
so percentage is calculated by dividing total by 4.
Now suppose we have a list that contains the marks of 4 subjects.
marks = [93, 34, 54, 67]
We want to send the marks of this list to the result function. Here, is the
function call for it.
result(marks[0], marks[1], marks[2], marks[3])
In this call, we are sending all four elements of the list to the function. There
is a simpler way of doing this in Python. We can write our call like this.
result(*marks)
This is equivalent to the previous call. We have added an asterisk before the
name of the list so the list is not sent as a single argument, it is unpacked
and the elements of the list become separate positional arguments of the
function. The four elements of the list are assigned to four parameters of the
function. So, instead of providing individual positional arguments in a
function call, you can provide a list, tuple or a set. And to tell the interpreter
that you are not sending the list, tuple or set as a single argument, you need
to add an asterisk before the list or tuple name.
If you don’t put the asterisk in the call, then you will get an error, because
now the function is getting only a single argument instead of four arguments.
result(marks) #Error
If the number of elements in the list that you are unpacking is not equal to
the number of parameters, then also you will get an error. For example,
suppose we have 6 elements in this list.
marks = [93, 34, 54, 67, 56, 89]
result(*marks)
We will get an error because 4 arguments were expected by the function but
6 were given. If we want to provide the first 4 elements from this list as
argument, then we can use the slicing operator to get a new list object which
will be unpacked.
marks = [93, 34, 54, 67, 56, 89]
result(*marks[:4])
So, if the number of elements in the list is more than the number of
arguments, then we can use slicing to extract the correct number of
arguments.
Now let us print the marks list using the print function.
marks = [93, 34, 54, 67, 56, 89]
print(marks)
Output
[93, 34, 54, 67, 56, 89]
There is nothing special in it, we have done it many times. Again, we will
send this list to the print function, but this time preceded with an asterisk.
print(*marks)
Output
93 34 54 67 56 89
We can see the difference between the two outputs. In the call
print(marks), the print function got only one argument of list
type, and in the call print(*marks), it got 6 arguments of int type.
Similarly, there is difference between the calls, print(’Hello’) and
print(*’Hello’). In the first call, print works only with one string
argument, while in the second call, the string is unpacked and the print
function gets 5 arguments.
If we use double asterisks before the argument in the call, then we can use a
dictionary to provide the keyword arguments. For example, suppose we have
a dictionary that contains the marks, and we want to calculate the result for
the marks from this dictionary by using our same result function.
marks2 = {'m1': 93, 'm2': 34, 'm3': 54, 'm4': 67}
def result(m1, m2, m3, m4):
total = m1 + m2 + m3 + m4
per = total / 4
print(f'Total Marks = {total}, percentage={per:.2f}%')
print('Pass' if per > 40 else 'Fail')
result(**marks2)
The dictionary marks2 has 4 pairs, and the names of keys are the same as
the names of parameters in the definition of function result, so we can
use this dictionary to send arguments to this function. Since marks2 is a
dictionary, we have to precede it with two asterisks to unpack it.
When an argument in a function call is preceded by a single asterisk (*), it
means that the argument is a list, tuple, set or string. Such an argument will
be unpacked and the contained values will be sent to the function as
positional arguments.
When an argument in a function call is preceded by a double asterisk (**), it
means that the argument is a dictionary. The dictionary will be unpacked and
its key value pairs will be used to provide keyword arguments to the
function.
So, we can use a single asterisk to unpack lists, strings or tuples for
providing positional arguments, and we can use double asterisk to unpack a
dictionary for providing keyword arguments. This is how we can unpack
arguments in the next section we will see how to pack arguments.
Variable Number of Positional Arguments
By now you must have called print() function many times, have you
noticed anything special about this print function that is not there in the
functions that we have written till now.
print(1, 2, 3)
print(L, d, x, y, z)
print('Hello', 'world')
print(x)
The print function can be called with any number of arguments. Similarly,
the built-in functions max, min or sum can also be called with any number
of arguments. The functions that we have written so far don’t have such
capability, now we will see how to create functions that can accept any
number of arguments.
We again take the function result that we saw in the previous section.
def result(m1, m2, m3, m4):
total = m1 + m2 + m3 + m4
per = total / 4
print(f'Total Marks = {total}, percentage={per:.2f}%')
print('Pass' if per > 40 else 'Fail')
This function has 4 parameters, so we can use it to find the result based on
marks of 4 subjects only. If we try to find the result based on marks of 3
subjects or 5 subjects or any other number of subjects, we get an error.
result(45, 78, 99) # Error
result(45, 78, 99, 77, 88) # Error
We want to make this function more flexible so that it works with any
number of arguments. For that we will need to make some changes in the
definition. In the function header, instead of 4 parameters, we will write a
single parameter, preceded by an asterisk.
def result(*args):
Now after this change in the function header, the function has become
capable of accepting variable number of arguments. All the arguments that
will be sent in the function call will be collected in a tuple named args.
And we can use that tuple inside the function body.
Now to calculate the total of all marks we will call the function sum.
total = sum(args)
To calculate percentage, we will divide total by len(args) instead of
dividing by 4. This is because now we don’t have just 4 subject marks, the
number of marks will be equal to the length of the tuple args.
per = total / len(args)
Rest of the code remains the same, we will print args in the beginning of
the function so that we can see that it is actually a tuple. Here is the modified
function.
def result(*args):
print(args)
total = sum(args)
per = total / len(args)
print(f'Total Marks = {total}, percentage={per:.2f}%')
print('Pass' if per > 40 else 'Fail')
result(23, 89, 77, 67, 89, 90)
result(67, 83, 68)
result(89)
Output
(23, 89, 77, 67, 89, 90)
Total Marks = 435, percentage=72.50%
Pass
(67, 83, 68)
Total Marks = 218, percentage=72.67%
Pass
(89,)
Total Marks = 89, percentage=89.00%
Pass
The function result can now be called with any number of arguments. It
can be called even without any arguments and in that case args will be an
empty tuple. In this case we will get a divide by zero error because we have
divided by the length of the tuple which is zero in this case.
So, to gather all the variable number of arguments, you just need to specify a
parameter with an asterisk in front of it. You can give this parameter any
other name of your choice, but conventionally it is named args.
When you need to write a function definition but you don’t know how many
arguments it will receive when it will be called, you can use this *args
parameter. You can combine this parameter with other parameters. For
example, suppose our result function has two more parameters name and
standard.
def result(name, standard, *args):
print(name, standard)
print(args)
total = sum(args)
per = total / len(args)
print(f'Total Marks = {total}, percentage={per:.2f}%')
print('Pass' if per > 40 else 'Fail')
result('Anu', 'VI', 34, 66, 88, 99, 344)
result('Dev', 'V', 99, 344)
Whenever this function is called, the first argument is assigned to name,
second argument is assigned to standard and then the rest of the
arguments, whatever be their number will be gathered in a tuple named
args. This way we can collect all the extra arguments using *args. So, we
can use other parameters with *args, but all those parameters should come
before *args.
Now suppose we have these lists named marks1, marks2, marks3 all of
different sizes.
marks1 = [23, 45, 67]
marks2 = [23, 45, 67, 89, 88, 99]
marks3 = [56, 77, 88, 22, 77]
In the function call, we can send a list by using the asterisk.
result('Anu', 'VI', *marks1)
This is what we had done in the previous section on unpacking arguments. In
this call, we have an asterisk before the argument named marks1 so it will
be unpacked. The list marks1 will be unpacked and its elements will become arguments of the call. This is actually equivalent to the this call.
result('Anu', 'VI', 23, 45, 67)
Now inside the function, these arguments will be packed in the tuple named
args. So now we can send a list of any size to this function which we were
not able to do it previously.
result('Anu', 'VI', *marks2) # Sending a list of 6 elements
result('Anu', 'VI', *marks3) # Sending a list of 5 elements
Let us see one more example. We will write a function definition to find
average of some numbers. We need this function to accept any number of
arguments, so we specify the parameter with asterisk.
def average(*args):
return sum(args) / len(args)
We can use this function to find the average of any number of arguments.
a1 = average(9, 2, 1)
a2 = average(3, 5, 6, 7, 8)
If we have a list or a tuple, then we can send it to this function by preceding
the name with an asterisk.
L = [1, 5, 7, 8, 0, 4, 2, 8, 5]
a3 = average(*L)
We can use this function to find average of values of a dictionary also, for
example suppose we have this dictionary.
d = {'John': 23, 'Ted': 25, 'Sam': 27, 'Nick': 21}
a4 = average(*d.values())
In this drive we saw how to provide a variable number of positional
argument values and we will see how to provide variable
number of keyword arguments in later sections.











