## Introduction

We've come across quite a few functions uptil this point in this course, including `print()`, `len()`, `input()`, `type()` and so on.

An intergral part of programming, functions are in the toolbox of nearly all programming languages in some way or the other. In this chapter we shall explore functions in Python, at the very basic level.

Some other concepts related to functions require you to know about other concepts in Python such as lists, dictionaries, etc. and would likewise be discussed in detail later in the functions unit, once you have the knowledge of these prerequisite concepts.

## What is a function?

Let's start by grasping the idea of a function.

A function is a process, an action, that is represented by a block of code and executed when the function is called.

Suppose you want to square a number `a` and then subtract from it another number `b`, and finally print the result.

To do this for 3 pairs of numbers you'll write the respective block of statements, that accomplishes this for one single pair of numbers, three times as shown below:

``````a = 10
b = 20
print(a ** 2 - b)

a = 5
b = -2
print(a ** 2 - b)

a = 10
b = -1
print(a ** 2 - b)``````

Imagine you had to do this 1000 times!

Well one way is to copy paste the first block of statements here (from line 1 to line 3) 999 times, changing the values of `a` and `b` for each new computation. But what if you decide to subtract the square root of `b` from twice that of `a`, instead of subtracting `b` from the square of `a`?

You can realise the problem...

We'd need to change the expression in all 1000 occurences! Even though, in this case, we might be able to get on with finding `a ** 2` and replacing it with `a * 2`, we won't be so lucky always! In fact, most of the times, this luck isn't there.

So how to solve this problem of grouping some code in one location, that would need to be changed just once to reflect changes everywhere where the piece of code is used?

A function solves this problem.

It can be used to define a block of code that can be reused again and again at will. Plus, since a function can be named (as we shall see below), the purpose of this block of code can be made apparent in the name of the function.

Let's see how to create a function in Python.

## Creating a function

We start off by writing the `def` keyword. The `def` keyword stands for 'define', and serves to define a function.

Then comes the name of the function. This name follows the same naming rules applicable to variables in Python, as we saw in the Python Variables chapter.

After the name, comes a pair of parentheses (`()`) and finally a colon (`:`). These parentheses are used to hold data used by the function, known as arguments. We'll see this later on.

Let's create a simple function:

``````def sayHello():
print('Hello World!')``````

The colon (`:`), used throughout Python, represents the start of a block of code. The block of code that follows is put at an increased level of indentation to imply that it belongs to a given statement.

In the case of a function, a colon represents the start of the block of code of the function.

Generally in other programming languages, a block of code is put in a pair of `{}` curly braces, however in Python it's put after a colon. This feature of Python greatly reduces code clutter and makes the code look elegant.

This block of code (for a function) is formally known as the function's body.

Here we can place any valid Python code, which obviously has something to do with the function.

Together the `def` statement and the function's body is referred to as the function's definition.

Once a function is created, it can be called - or much better to say, it can be invoked. Calling a function simply means to execute its body.

A function is called by writing the name of the function followed by a pair of parentheses.

Based on all this information, we'll now solve the computation problem we saw at the start of this section, with the help of a function.

First we have to settle on a name for the function. Let's call it `compute_and_print()`. Shown below is it's definition.

``````def compute_and_print():
print(a ** 2 - b)``````

Inside the function, we print `a ** 2 - b`, which is exactly the same expression we were printing in the code at the start of this section.

Let's call this function a couple of times:

``````a = 10
b = 20
compute_and_print() # 80

a = 5
b = -2
compute_and_print() # 27``````

Here's what happens in the first 3 lines:

1. First a variable `a` is defined with the value `10`.
2. Then, another variable `b` is defined with the value `20`.
3. Finally, the function `compute_and_print()` is called. Python's execution shifts to the body of the function `compute_and_print()`. Here, the expression `a ** 2 - b` is evaluated, with `a = 10` and `b = 20`, and its result is printed.

The same process gets repeated for lines 5 - 7, this time with `a = 5` and `b = -2`.

Now if we want to change the expression that process the numbers `a` and `b`, we just need to go to the function `compute_and_print()` and change its body.

Consider the code below:

``````def compute_and_print():
print(a ** 2 + b ** 2)

a = 10
b = 20
compute_and_print() # 500

a = 5
b = -2
compute_and_print() # 29``````

Nothing is changed except for the line highlighted.

This is the power of functions - or better to say, a glimpse into the power of functions!

## Function arguments

Although we are familiar with the term argument from the Python Basics chapter, we don't know how to put its essence into our applications.

Let's review what is an argument:

An argument is data that we provide to a function so that it can use it to carry out its desired job.

Recall the `print()` function.

When we pass in an argument, the function print it. That is, the function utilises the data we pass on to it for its desired job, which is to print stuff to the shell.

Arguments are a commonly used feature of functions not just in Python, but in almost all programming languages. Let's see how to use them in our own functions.

To get a function to accept a given argument we first need to set up a parameter on it.

A parameter is the name with which a given argument can be referred to in the function body.

A parameter can be thought of as a variable holding a given value. In fact, as we shall see later on, it is a variable, known as a local variable.

Parameters go inside the parentheses in the function's definition. To add a parameter to a function, we first come up with a name for the parameter (which must follow the varible naming rules in Python), and then write that inside the pair of parentheses.

Multiple parameters can be added by separating them with a `,` comma.

Consider the example below, where we redefine our old `compute_and_print()` function using parameters:

``````def compute_and_print(a, b):
print(a ** 2 - b)``````

This time, the function has two parameters `a` and `b` that are evaluated in the expression `a ** 2 - b`, which is ultimately printed by the function.

Let's call this function with some arguments:

``````compute_and_print(10, 20) # 80

compute_and_print(5, -2) # 27``````

Here's what happens during the execution of the first statement:

1. `compute_and_print()` is called with two arguments, `10` and `20`.
2. Execution shifts to the body of `compute_and_print()`, where the parameter `a` gets the value `10` and `b` gets the value `20`. Remember, parameter values are resolved by order i.e the parameter that comes first holds on to the first argument.
3. The expression `a ** 2 - b` is evaluated using these values of `a` and `b` and the result is finally printed.

Everything is the same here, except for the identifiers `a` and `b`. Previously, these were defined outside the function's invocation expression, as global variables, but now they are defined inside the function, as its parameters.

This dramatically simplifies our previous piece of code, where we were redefining the variables `a` and `b` again and again and then calling `compute_and_print()`. With the parameters defined, we just have to call `compute_and_print()` with given values and wait for the computed result to be printed.

Functions are just amazing!

## Moving on..

The landmarks of Python functions don't end here. There is still an array of concepts left to be looked into, such as default arguments, list arguments, dictionary arguments, lambdas, local variables, and much more.

All this will be discussed later on in the Python Functions unit, since we have to fully understand many other basic concepts before we can fully comprehend all these minute concepts of functions.

This chapter was meant to give you the momentum to construct functions in Python which is extremely vital for comprehending the content of the next chapters.