Python offers a number of options in terms of the way arguments can be passed to functions. Each method of argument passing tries to answer the following question:
How are the arguments in the function call passed to the parameters in the function definition?
All functions that we have seen so far have used positional arguments. Here, the position of an argument in the function call determines the parameter to which it is passed. Let us take the following problem:
Write a function that accepts three positive integers
x
,y
andz
. ReturnTrue
if the three integers form the sides of a right triangle withx
andy
as its legs andz
as the hypotenuse, andFalse
otherwise.
Solution
xxxxxxxxxx
71def isRight(x, y, z):
2 if x ** 2 + y ** 2 == z ** 2:
3 return True
4 return False
5
6print(isRight(3, 4, 5)) # 3 is passed to x, 4 is passed to y, 5 is passed to z
7print(isRight(5, 4, 3)) # 5 is passed to x, 4 is passed to y, 3 is passed to z
The output is:
xxxxxxxxxx
21True
2False
Arguments are passed to the parameters of the function based on the position they occupy in the function call. Look at the comments in the above code to get a clear picture. Positional arguments are also called required arguments, i.e., they cannot be left out. Likewise, adding more arguments than there are parameters will throw an error. When positional arguments are involved, there should be exactly as many arguments in the function call as there are parameters in the function definition. Try to execute the following code and study the error message:
xxxxxxxxxx
41##### Alarm! Wrong code snippet!
2isRight(3, 4)
3isRight(3, 4, 5, 6)
4##### Alarm! Wrong code snippet!
Keyword arguments introduce more flexibility while passing arguments. Let us take up the same problem that we saw in the previous section and just modify the function calls:
xxxxxxxxxx
31# The following is just a function call.
2# We are not printing anything here.
3isRight(x = 3, y = 4, z = 5)
The function call in line-3 uses what are known as keyword arguments. In this method, the names of the parameters are explicitly specified and the arguments are assigned to it using the =
operator. This is different from positional arguments where the position of the argument in the function call determines the parameter to which it is bound. One advantage of using keyword arguments is that it reduces the possibility of entering the arguments in an incorrect order. For example:
xxxxxxxxxx
41isRight(3, 4, 5) # intended call
2isRight(5, 4, 3) # actuall call
3isRight(x = 3, y = 4, z = 5) # same as intended call
4isRight(z = 5, y = 4, x = 3) # same as intended call
Keyword arguments and positional arguments can be combined in a single call:
xxxxxxxxxx
11isRight(3, y = 4, z = 5)
Now try this out:
xxxxxxxxxx
31#### Alarm! Wrong code snippet! ####
2isRight(x = 3, 4, 5)
3#### Alarm! Wrong code snippet! ####
The interpreter throws a TypeError
with the following message: positional argument follows keyword arguments
. That is, in our function call, the positional arguments — 4
and 5
— come after the keyword argument x = 3
. Why does the interpreter objects to this? Whenever both positional and keyword arguments are present in a function call, the keyword arguments must always come at the end. This is quite reasonable: positional arguments are extremely sensitive to position, so it is best to have them at the beginning.
How about the following call?
xxxxxxxxxx
31#### Alarm! Wrong code snippet! ####
2isRight(3, x = 3, y = 4, z = 5)
3#### Alarm! Wrong code snippet! ####
The interpreter objects by throwing a TypeError
with the following message: isRight() got multiple values for argument x
. Objection granted! Another reasonable requirement from the Python interpreter: there must be exactly one argument in the function call for each parameter in the function definition, nothing more, nothing less. This could be a positional argument or a default argument, but not both.
Consider the following scenario. The image that you see here is a map of your neighborhood. The grid lines are roads that can be used by cars. You wish to reach the point from . There are no restrictions if you are on foot. The easiest way is to move along the line . This is called the Euclidean distance between points and . If you are in a car, then you are forced to move along the grid lines. The distance you would have to cover in a car is . This distance is called the Manhattan distance between points and .
Let us say that a self-driving car startup operating in your neighborhood uses both these metrics while computing distances. Assume that its code base invokes the Euclidean distance 10 times and the Manhattan distance 1000 times. Since these metrics are used repeatedly, it is a good idea to represent them as functions in the code base:
xxxxxxxxxx
71# Assume that O is the origin
2# All distances are computed from the origin
3def euclidean(x, y):
4 return pow(x ** 2 + y ** 2, 0.5)
5
6def manhattan(x, y):
7 return abs(x) + abs(y)
While the above code is fine, it ignores the fact that the Manhattan distance is being used hundred times more frequently compared to the Euclidean distance. Default arguments can come in handy in such situations:
xxxxxxxxxx
51def distance(x, y, metric = 'manhattan'):
2 if metric == 'manhattan':
3 return abs(x) + abs(y)
4 elif metric == 'euclidean':
5 return pow(x ** 2 + y ** 2, 0.5)
The parameter metric
has 'manhattan'
as the default value. Let us try calling the function without passing any argument to the metric
parameter:
xxxxxxxxxx
11print(distance(3, 4))
This gives 7
as the output. Since no value was provided in the function call, the default value of 'manhattan'
was assigned to the metric
parameter. In the code base, wherever the Manhattan distance is invoked, we can just replace it with the function call distance(x, y)
.
The following points are important to keep in mind:
Let us illustrate some of these points:
xxxxxxxxxx
71#### Alarm! Wrong code snippet! ####
2def distance(metric = 'manhattan', x, y):
3 if metric == 'manhattan':
4 return abs(x) + abs(y)
5 elif metric == 'euclidean':
6 return pow(x ** 2 + y ** 2, 0.5)
7#### Alarm! Wrong code snippet! ####
The above code throws a SyntaxError
with the following message: non-default argument follows default argument
. In the function definition, the default parameter must always come at the end of the list of parameters. Now, for different ways of passing arguments in the presence of default parameters:
xxxxxxxxxx
31distance(3, 4)
2distance(3, 4, 'manhattan')
3distance(3, 4, metric = 'manhattan')
All three function calls are equivalent. The first one uses default value of metric
. The second call explicitly passes 'manhattan'
as the metric using a positional argument. The last call explicitly passes 'manhattan'
as a keyword argument.
Consider the following code:
xxxxxxxxxx
81def double(x):
2 x = x * 2
3 return x
4
5a = 4
6print(f'before function call, a = {a}')
7double(a)
8print(f'after function call, a = {a}')
The output is:
xxxxxxxxxx
21before function call, a = 4
2after function call, a = 4
We see that the value of a
is not disturbed by the function in any way. When the function call double(a)
is invoked, the value in a
is assigned to the parameter x
in the function. Arguments are passed by assignment in Python, which means that something like x = a
happens when double(a)
is invoked. This kind of a function call where the value in a variable is passed as argument to the function is called call by value.
Consider the following code:
xxxxxxxxxx
51def square(x):
2 return x * x
3
4x = 10
5x_squared = square(x)
We are using the same name for both the parameter of the function square
and the argument passed to it. This is a bad practice. It is always preferable to differentiate the names of the parameters from the names of the arguments that are passed in the function call. This avoids confusion and makes code more readable. At this stage, you might be wondering how the variable x
inside the function is related to the variable x
outside it. This issue will be taken up in the next lesson on scopes. The above code could be rewritten as follows:
xxxxxxxxxx
51def square(num):
2 return num * num
3
4x = 10
5x_squared = square(x)