Python — Function and Function Parameters

Pinaki Mishra
Analytics Vidhya
Published in
10 min readApr 27, 2020

--

Knowledge is an ocean that has no coast

In a nutshell when we defined a function, we may or may not pass parameters. But most of the time parameters are passed while defining a function. Semantically we defined function as below:

def myfunction(a, b):
#some code

So when we call the function,

x = 10
y = 20
myfunction(x, y)

In this context x and y are arguments of myfunction and most importantly note that x and y are passed by reference. i.e. the memory address of x and y are passed.
We can pass positional and keyword arguments to a function.

Positional and Keyword Arguments

We normally assign the arguments to parameters of a function in the order in which they are passed, i.e. the position of the parameter.

Let’s define a function, which takes two arguments and return the concatenation of two strings.

def concatenate(a: str, b: str) -> str:
return a + b

So when we call above function by passing arguments,

concatenate("hello", "world!!")

Where the first argument “hello” assigned to first parameter i.e. a and second argument “world!!”assigned to second parameter i.e. b.

We can make positional argument as optional by specifying a “Default” value for the corresponding parameter. Let’s modify our concatenate function by adding additional parameter,

def concatenate(a: str, b: str, c: str = "Welcome") -> str:
return a + b + c

We added third variable “c” with default value as “Welcome”. So if we call above function without passing third argument, then default value for third positional parameter will be referred.

So if we call the above function,

print(concatenate("hello", " world!! "))

The output will be:

hello world!! Welcome

Now if we pass third argument then the function will not consider the default value of third parameter (which is obvious 😊). To illustrate let’s modify our code :

print(concatenate("Hello, "," this is my first story in ", " Medium"))

When we run the above code, the output will be:

Hello, this is my first story in Medium

So far so good and pretty straightforward to understand. However, there might be a situation where we have function, in which any one of the parameter is optional. To be more precise, let’s say we have addition function which have three parameters and one of them or to simplify the second positional parameter is optional.

def addition(a: int, b: int = 150, c: int) -> int:
return a + b + c

If we call this function by passing only two arguments, for example:

print(addition(150,150))

then python will not able to identify whether the second argument i.e. 150 refers to second parameter i.e. b or third parameter i.e. c. So it will throw error while executing.

The error could be something like:

def addition(a: int, b: int = 150, c: int) -> int:
^

SyntaxError: non - default argument follows default argument

So, in order to avoid such error, we need to follow following rule while passing positional parameter with default value.

If we defined a positional parameter with default value then for every positional parameter(s) after it we must also be given default value.

So we need to modify our addition function by assigning default value to third parameter i.e. c

def addition(a: int, b: int = 150, c: int = 0) -> int:
return a + b + c

Now when we run below code:

print(addition(200, 300)) # a = 200, b = 300, c = 0

Output will be:

500

For the addition function , we can now pass one argument as well. For example:

print(addition(1)) # a = 1, b = 150 (Default), c = 0(Default)

Output will be:

151

Now what if you want to skip the second second argument and pass first and third arguments to addition function. We can achieve this by using “Keyword Arguments” which also known as “Named Arguments”.

Using Keyword or named arguments, we can invoke the function by passing 1st and 3rd arguments leaving default values for 2nd argument.

print(addition(a=1000, c=1500)) # So b = 150 (default value)

Output will be:

2650

Notes:

1. We can specify positional arguments by using their parameter name, no matter they have assigned values or not.

Example: Let’s define a function which takes 4 parameters and return all 4 values passed to the function.

def returning_value(val1, val2, val3, val4):
return "val1 = {0}, val2 ={1}, val3 ={2}, val4 ={3}".format(val1, val2, val3, val4)

You can call the function either passing all the four arguments without using Keyword arguments or with keyword arguments.

# Passing arguments without keywords argument
print(returning_value(1, 2, 3, 4))

Output will be:

val1 = 1, val2 =2, val3 =3, val4 =4

# Passing arguments with keywords argument
print(returning_value(val4=400, val1=120, val3=300, val2=140))

Output will be:

val1 = 120, val2 =140, val3 =300, val4 =400

If we are using keyword arguments, then order of passing parameter does not matter and that is the advantage of using keyword arguments.

However there is a caveat when using Keyword argument.

2. If you started using keyword argument then all arguments thereafter must be keyword arguments.

For example:

# Passing few argument with keyword
print(returning_value(val1=100, 200, val3=300, 400))

When we run the above code, below error message will display:

print(returning_value(val1=100, 200, val3=300, 400))
^
SyntaxError: positional argument follows keyword argument

Arbitrary Arguments (*args)

When we define a function with parameters and access the function with arguments then eventually we are accessing the parameters by their relative positions.

For example, my_returnargs function takes 3 positional arguments and returns these value.

def my_returnargs(val1, val2, val3):
print("First Positional Value = ", val1)
print("Second Positional Value = ", val2)
print("Third Positional Value = ", val3)

Let’s define a list variable which has three values:

list = ['Hello', 'World', 'Learn Python']

Now let’s pass list as an arguments to my_returnargs function, rather we will unpack list so that all the values will be passed as an argument:

my_returnargs(*list)

The output will be

First Positional Value = Hello
Second Positional Value = World
Third Positional Value = Learn Python

In the above example we have 3 arguments passed in the function and since list has 3 elements so when unpacked all the three arguments assigned with value.

Now what if we have same list which has more than 3 elements and tries to unpack in function arguments then what will happen ?

list = ['Hello', 'World', 'Learn Python', 'Some Extra Arguments']

my_returnargs(*list)

So when we execute the above code, python will throw error:

my_returnargs(*list)
TypeError: my_returnargs() takes 3 positional arguments but 4 were given

We can avoid such error by passing arbitrary arguments (*args) in the function. We can modify the function to take two positional arguments and post that arbitrary arguments.

def my_returnargs(val1, val2, *args):
print("First Positional Value = ", val1)
print("Second Positional Value = ", val2)
print("Arbitrary Argument Values = ", args)

Now let’s run the same code where we have unpacked the list with 4 values

list = ['Hello', 'World', 'Learn Python', 'Some Extra Arguments']

my_returnargs(*list)

The output will be:

First Positional Value = Hello
Second Positional Value = World
Arbitrary Argument Values = (‘Learn Python’, ‘Some Extra Arguments’)

Note: The args argument returns the value as tuple

Also you can not add any positional argument after the *args. Which means *args eventually exhaust all the positional arguments.

We can define a function by passing a positional arguments after *args. Python will never complain about while defining such function :)

def my_returnargs(val1, val2, *args, val3):
print("First Positional Value = ", val1)
print("Second Positional Value = ", val2)
print("Arbitrary Argument Values = ", args)
print("Another positional argument Value = ", val3)

The problem will occur when we call the function by passing arguments and python will throw TypeError

list = ['Hello', 'World', 'Learn Python', 'Some Extra Arguments']

my_returnargs(*list)

Output:

my_returnargs(*list)
TypeError: my_returnargs() missing 1 required keyword-only argument: 'val3'

However, we can get rid of such issue by enforcing user to pass mandatory keyword arguments.

Keyword Arguments *

The question will always crops that when can we use mandatory keyword arguments ? So the answer is, once we exhausts out of all the positional arguments.

To illustrate let’s see below example:

def my_returnargs(val1, val2, *args, val3):
print("First Positional Value = ", val1)
print("Second Positional Value = ", val2)
print("Arbitrary Argument Values = ", args)
print("Another positional argument Value = ", val3)


list = ['Hello', 'World', 'Learn Python', 'Some Extra Arguments']

my_returnargs(*list, val3 = "This is a keyword argument") # Explicitly pass the keyword argument

When we run the above code the out put will be:

First Positional Value = Hello
Second Positional Value = World
Arbitrary Argument Values = (‘Learn Python’, ‘Some Extra Arguments’)
Another positional argument Value = This is a keyword argument

We can restrict function explicitly not to pass any positional argument by passing * as parameter. For example:

def no_positionalarg(*, val: str) -> str:
print("Only Keyword Argument :", val)

If we try to pass positional argument along with Keyword argument then python will through TypeError

no_positionalarg(10,20,val = "Hello World!!")

The Output will be:

no_positionalarg(10,20,val = "Hello World!!")
TypeError: no_positionalarg() takes 0 positional arguments but 2 positional arguments (and 1 keyword-only argument) were given

But if we pass only Keyword argument then Python will not show any error

no_positionalarg(val = "Hello World!!")

The Output will be:

Only Keyword Argument : Hello World!!

Now let’s combine positional arguments, optional positional arguments, *args, no positional argument and mandatory keyword arguments.

def combine_func(val1, val2=50, *args, val3, val4="Hello World!!!"):
print("val1 = ", val1)
print("val2 = ", val2)
print("args = ", args)
print("val3 = ", val3)
print("val4 = ", val4)

In the above function, we have :

· Mandatory Positional parameter i.e. val1

· Optional Positional parameter i.e. val2

· Optional arbitrary number of positional arguments i.e. *args

· Mandatory Keyword Parameter i.e. val3

· Finally optional positional parameter i.e. val4

So when we call the function:

list = [101, 'Python', 'Hello', 'Welcome', 20, 30]
combine_func(*list, val3="This is a mandatory keyword arguments after args")

And the output will be:

val1 = 101
val2 = Python
args = (‘Hello’, ‘Welcome’, 20, 30)
val3 = This is a mandatory keyword arguments after args
val4 = Hello World!!!

What if we restrict no positional arguments followed by mandatory keyword arguments and then optional positional argument within a function.

Let’s modify the same function as below:

def combine_func(val1, val2=50, *, val3, val4="Hello World!!!"):
print("val1 = ", val1)
print("val2 = ", val2)
print("val3 = ", val3)
print("val4 = ", val4)

In the above function, we have :

· Mandatory Positional parameter i.e. val1

· Optional Positional parameter i.e. val2

· No positional arguments i.e. *

· Mandatory Keyword Parameter i.e. val3

· Finally optional positional parameter i.e. val4

So when we call the function by passing three positional arguments followed by keyword arguments

combine_func('test',25,1000,c=100.1005)

then we will encounter below error:

combine_func('test',25,1000,val3=100.1005)
TypeError: combine_func() takes from 1 to 2 positional arguments but 3 positional arguments (and 1 keyword-only argument) were given

The reason is the first two arguments refer to positional arguments i.e. val1 and val2 (though val2 is optional positional argument which has default value of 50 but here we pass 25.) . However when we pass the third argument i.e. 1000 before keyword argument i.e. val3 then the Python could not process as the function is defined as after the two positional arguments there should not be any positional arguments to be passed rather mandatory keyword argument i.e. val3 and followed by another optional positional parameter.

Let’s modify the arguments while calling the function as below:

combine_func('test',25,val3=100.1005, val4 = "Welcome to pythonic way of writing python")

Output will be:

val1 = test
val2 = 25
val3 = 100.1005
val4 = Welcome to pythonic way of writing python

Quite interesting but very important (more specifically it’s a caveat ), if you call a function with named or keyword arguments though you declare these as positional argument followed by *args then Python will throw Syntax error stating Positional argument follows keyword argument.

To illustrate this , let’s run below code:

def namedargument_with_args(val1, val2, val3=10, *args):
print(val1, val2, val3, *args)


namedargument_with_args(val1=1, val2=2, 30, 50, 60, 80)

So when we run the above code, below error will display:

namedargument_with_args(val1=1, val2=2, 30, 50, 60, 80)
^
SyntaxError: positional argument follows keyword argument

What if we call the function and tries to pass the option argument at end of *args. Well certainly Python will throw error as it finds multiple values of positional argument val3.

def namedargument_with_args(val1, val2, val3=10, *args):
print(val1, val2, val3, *args)


namedargument_with_args(1,2, 30, 50,60,80, val3= 20)
>>> Output:
namedargument_with_args(1,2, 30, 50,60,80, val3= 20)
TypeError: namedargument_with_args() got multiple values for argument 'val3'

Arbitrary number of keyword arguments (**kwargs)

In python we can add arbitrary number of keyword arguments to a function using **kwargs.

User can specify **kwargs even though the positional arguments are not exhausted (which is not true for *args). However, there should not be any positional parameters that can come after **kwargs.

But there is a caveat to **kwargs. If we declare a function where the first parameter is * i.e. no positional arguments followed by **kwargs then Python will not process rather throws a syntax error:

def kwargs_function(*,**kwargs):
print (kwargs)
>>> Output:
def my_func(*,**kwargs):
^
SyntaxError: named arguments must follow bare *

However, the above function will work if we pass mandatory keyword or optional positional argument followed by **kwargs.

def kwargs_function(*,val,**kwargs):
print ("The keyword argument value = ", val)
print ("Arbitrary Keyword Arguments are: ", kwargs)

kwargs_function(val= "Hello World!", val1 = 1, val2 = 2, val3 = 3, val4 = 4, val5 = 5, val6 = "Welcome to Medium!!!!")
>>> Output:
The keyword argument value = Hello World!
Arbitrary Keyword Arguments are: {'val1': 1, 'val2': 2, 'val3': 3, 'val4': 4, 'val5': 5, 'val6': 'Welcome to Medium!!!!'}

Note: The kwargs argument returns the value as dictionary

We can combine both *args and **kwargs within a function.

def args_kwargs_func(*args,**kwargs):
print("Arbitrary arguments values :", args)
print ("Arbitrary Keyword Arguments are: ", kwargs)

args_kwargs_func(10, 20, 30 , 40 ,50 ,a = 1, b = 2, c = 3, d = 4, e = 5, f = "Hello World!!" )
>>> Output
Arbitrary arguments values : (10, 20, 30, 40, 50)
Arbitrary Keyword Arguments are: {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 'Hello World!!'}

We can declare a function with positional argument, arbitrary number of positional arguments (i.e. *args) , mandatory keyword only arguments and arbitrary keyword arguments.

def my_func(arg_1, arg_2, *args, kwarg_1, **kwargs):
print(arg_1)
print(arg_2)
print(args)
print(kwarg_1)
print(kwargs)


s1 = "Welcome"
s2 = "Python"
tuple_1 = (1, 2, 3, 4, 5, 6, "Hello World!!!")
my_func(s1, s2, *tuple_1, kwarg_1="Mandatory Keyword Argument", a=10, b=20, c="Hello", d="World!")
>>> Output
Welcome
Python
(1, 2, 3, 4, 5, 6, 'Hello World!!!')
Mandatory Keyword Argument
{'a': 10, 'b': 20, 'c': 'Hello', 'd': 'World!'}

We can pass positional, keyword as well as arbitrary arguments & arbitrary keyword arguments to a function while defining. However we need to take care of the way we need to pass these parameters i.e. keeping certain rules in mind while defining any function.

Post your queries / thoughts and suggestion as well :)

Happy learning!!!

--

--