# User defined functions

## Contents

### What is a user defined function

Let’s take a simple task – Calculate the interest on the balance in your bank account since the beginning of the year. In order to do that, you first need 3 things

- account balance
- interest rate
- days since Jan 1st of the current year

def calculate_interest(balance,interest,days) : interest_amount = balance * ( interest / 100 ) * ( days/365 ) return interest_amount

To call the function, all you have to do is to just follow the signature of the function.

interest_amount = calculate_interest(1200,10,100) print ( interest_amount )

32.87671232876712

Think of functions as essentially outsourcing the processing to a different place. In a large project, each developer works on developing his/her own piece of logic. Eventually, all of them need to be linked together. Functions provide the most fundamental way in which logic can be clealy separated. Ofcourse, there are other things like modules, libraries etc that do the same at a much higher level, but that is a topic for another day.

Now, since the logic is separated into a separate function, the developer writing the function has to provide some kind of documentation on how to use the function. For example, in this case, the developer would have to provide some basic info on how to pass the arguments. For example, should the interest rate be passed as a percentage or value. How should the days be calculated – since the beginning of the fiscal year or the beginning of financial year etc.

In order to do this, python provides something called as a docstring.

### docstring

docstring is a simple way to provide documentation on the function. It can also be used for providing documentation on classes of modules as we will see when we get to them. Providing a docstring in a function is very simple – just provide the text enclosed in **triple quotes**.

def calculate_interest(balance,interest,days) : """ Calculates interest on a given balance. The key arguments are 1. Balance - Amount on which balance needs to be calculated 2. Interest - Annual interest in percentage 3. Days - Number of days since the beginning of the year """ interest_amount = balance * ( interest / 100 ) * ( days/365 ) return interest_amount

calculate_interest

<function __main__.calculate_interest(balance, interest, days)>

You can also get the same programmatically using the **doc** function.

print ( calculate_interest.__doc__ )

Calculates interest on a given balance. The key arguments are 1. Balance - Amount on which balance needs to be calculated 2. Interest - Annual interest in percentage 3. Days - Number of days since the beginning of the year

### Arguments

Arguments are how the function receives information from the caller. There are many variations in the way the arguments can be passed. For example, you can

- specify mandatory arguments
- pass default values to arguments
- have variable number of arguments and so on.

Let’s discover each of these variations.

### Required arguments

Most of the times some of the arguments to a function are required and some are not. Let’s take the **calculate_interest** example that we have seen above. There are 3 arguments.

def calculate_interest(balance,interest,days) : interest_amount = balance * ( interest / 100 ) * ( days/365 ) return interest_amount

What happens when you call the function with just 2 arguments ? Python throws a **TypeError**.

interest_amount = calculate_interest(1000, 5)

--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-22-b464ba338fc0> in <module> ----> 1 interest_amount = calculate_interest(1000, 5) TypeError: calculate_interest() missing 1 required positional argument: 'days'

When you don’t specify argument names, the position of the arguments is important.

interest_amount = calculate_interest(1000, 5,100)

### Named Arguments

Named arguments can be passed in any order you like – as long as you specify the argument name Python does the matching automatically.

interest_amount = calculate_interest(interest = 5, days = 100, balance = 1000)

This would result in the sa

The reason is because, unless specified otherwise, all arguments to a function are mandatory. Sometimes, to simplify things, we might make it easy on the user of the function to default an argument to a fixed value.

### Default arguments

Say, the interest rate is 5% unless specified otherwise. This is how we would define that function.

def calculate_interest (balance,interest = 5,days) : interest_amount = balance * ( interest / 100 ) * ( days/365 ) return interest_amount

File "<ipython-input-11-c745ff0a87a1>", line 1 def calculate_interest (balance,interest = 5,days) : ^ SyntaxError: non-default argument follows default argument

That didn’t seem to work, right ? What python is saying is that default arguments should be specified at the end (before the non-default argument begins). In this case, specify the *days* argument before the *interest* argument.

def calculate_interest (balance,days, interest = 5) : interest_amount = balance * ( interest / 100 ) * ( days/365 ) return interest_amount

Now, if we call the function, we don’t need to pass the argument for *interest*.

interest_amount = calculate_interest(1200, 100) interest_amount

16.43835616438356

However, if you want, you can very well pass the argument for interest as well. The function works either way. Very convenient, isn’t it ?

A good example for this is the **print** function. Look at the signature of it.

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

The arguments for separator (sep), end of line (end), output stream (file) etc are all defaulted to some values so that you don’t have to specify them everytime. However, you can very well send them in based on your requirements. For example, if you don’t want the newline to be printed for everytime the print function is used, send in a blank for the *end* argument.I

print ( "Ajay", end="") print ( "Tech")

AjayTech

If not for that, this is how the default behaviour would look like.

print ( "Ajay") print ( "Tech")

Ajay Tech

### Variable number of arguments

The print function is a good example of a function with variable number of arguments. It can take as many strings as you want and still print all of them sequentially.

print ( "Ajay", "Tech", "is", "a","tech","company")

Ajay Tech is a tech company

The way to define a function like this is by using a star ( ***** ) before the parameter name. Here is an example of a function that takes in a variable number of grades and computes the average of them.

def avg_grade(*grades) : i = 0 sum = 0.0 for grade in grades : sum = sum + grade i = i + 1 avg = sum / i return avg

avg = avg_grade(2.5,3.0,3.5) # as many arguments as you like avg

3.0

Internally, python takes in these arguments as a tuple. We will discuss tuples in python in a later section.

### Challenges

codeTake a string as an argument and count the length of the string using a for loop and return the length. While counting the characters, exclude the count if the character is a space.

Challenge: Create a python function to return the length of a string excluding the number of blank characters

def str_len ( string ) : length = 0 for char in string : if char != " " : length = length + 1 return length

codeThe function should take in 2 numbers. The first parameter is sent by the user and the second parameter should be defaulted to 5. Both numbers should be multiplied and sent back. If the user sends a number other than 5 as the second parameter that number should be used as the multiplication factor.

Challenge: Create a python function that takes in a number and multiples it by another number. By default the multiplication factor should be 5. If the user specifies one, it should be used.

def multiplier ( num_1, num_2 = 5) : return num_1 * num_2

codeThe function should take in a variable number of numbers as arguments and returns the sum of the numbers. Do not use a list to send the arguments.

Challenge: Create a python function that takes in any number of numbers and returns the sum of them.

def sum_numbers ( *numbers) : sum = 0 for number in numbers : sum = sum + number return sum