Functions in Python

Functions are blocks of codes which execute when it is called by its name. Data is assigned into a function as parameters. The function then returns data as result, which is executed as instructed in the body. Functions are used when a set of instructions has to be used again and again repeatedly. 

How to define and call a function in Python

Function in Python is defined by the “def” statement followed by the function name and parentheses (()).

There are three types of functions in Python:

  • Built-in functions, such as help (), min (), print (), etc..
  • User-Defined Functions (UDFs), made by users. 
  • Anonymous functions, also called lambda functions which are not declared with the standard def keyword and it also does not return any value.

How to write a function?

  • Any arguments or input parameters should be placed within parentheses
  • The code is written within every function starts with a def function_name (parameters) and then a colon (:). After that body starts and this body should be indented.
  • The statement return, exits a function, optionally passing back a value to the caller. A return statement with no arguments or parameters returns a value called None.

Functions vs. Methods

A method refers to a function which is part of a class. You can access it with an instance or object of the class. All methods are functions, but not all functions are methods. Now this definition at this point is something weird with heavy words. Class, method, instance, these words are tough to grasp for a new programmer. Let’s first see what is the difference between function and method?

In the first example, we are using sort () and in the second example sorted (). This word sort () is method and sorted () is function. This is not a definition. It’s just a difference to let you recognize that OK, this is a method and this is a function. Function sorted () will not change the original list, sort () will change the original list also. We have also stated that all methods are functions, but not all functions are methods. If we don’t know about method or function, we will say, sort () is a function and sorted () is also a function. After knowing this, now you can differentiate the two words.

Let’s take a non-technical example. Animals are a class. How will you determine if an animal is an object (instance) Dog? One method is voice: bark. Class is not our topic in this book. We will cover that in our next volume, when we will take the OOPS concept.

Parameters vs. Arguments

Parameters are the names used when defining a function or a method, and into which arguments are mapped. Arguments are the things which are supplied to any function or method call, while the function or method code refers to the arguments by their parameter names. In functional code, the output value of a function depends only on its arguments, so calling a function with the same value for an argument always produces the same result. Any programming language that follows this style of building structure and elements is called functional programming. This is the meaning of the functional programming word used in the definition of Python. In this, programming is done with expressions or declarations instead of statements. 

When to use return?

Using a return value is the trickiest part to understand. Just keep in mind that whenever there is an operation to be performed, then return is used. At this point, it’s time to start writing our function and understand this entire thing. Let’s write a function which will square the given number with 2. We need def followed by a function name, say, sq, followed by the number, n, in parenthesis.

We are squaring n**2, it means we are performing some operation on n. In this case we need to return the operation back to our function name. And when we have to display the result, we have to write the complete function name, like here we are writing, sq(2), and not only, 2 or sq(). One more thing to understand is that when we write sq(n), here, n is a parameter. But when I have assigned it some value, like sq(2), then it is termed as an argument. We can also write this function in a different way. 

What I have done is that I am assigning my function name to a variable f. I am printing my function without parentheses, that is, I am not executing it and returning any value. You can observe that function sq is somewhere in the memory. Can this variable f act as my function now?

Yes, it’s working like my main function sq. It means that we are assigning a function to a variable. But we are not assigning the result of a function to a variable. This variable f can support all the operations now which are available to other entities, and these operations include being passed as an argument, returned from a function, and assigned to a variable. This concept is known as First Class Function, and the object is known as First Class Citizen or First Class Object.  We can also pass the functions as arguments and return functions as a result of other functions. And when a function can pass other functions as arguments and return functions as a result of other functions, then these functions are called Higher- order functions.  Let’s write a function that will pass another function as an argument and will return the result. We will take a function as an argument and then we will take an array of integers as an argument. Then, we will map it. The map is an inbuilt function and later in this chapter we will read about it. But this map function which we are using now is not the built-in map function. We have done a similar type of coding in lists.  Let’s code it.

Now this is great. I have a variable called squares and I am assigning my_map_func to it and then passing the function square as an argument into it. Do remember that these are without parentheses or the function will try to execute. What if I had straight away passed the list as input argument?

In such cases, first class functions are helpful. Let’s see how to return a function as result. 

We have a function log_in with a message as it’s parameter. Then we have another function called login_msg with no parameters. We are simply printing something and then returning our function. Avoid parentheses or it’ll start executing at that time. Now we are assigning our function to a variable. We saw that we can convert our variable into a function like this and then we are returning this variable as a result of the function and this itself is a function. This is the significance of First Class Function.

Coming back to our programming practice, now I am writing a program to find the LCM of two numbers. How does LCM work? There are many techniques and you can follow any of them. I am explaining the method which I found quite easy. I am not going into the maths of finding LCM here. I know that everyone can find LCM using maths. There are two short tricks to find LCM. If there are two numbers, say, 2, 4, then LCM is 4. If numbers are 4, 8, then LCM is 8. The first trick is that among the two numbers, LCM is always the greater number. But this doesn’t work in maximum cases. Like, LCM of 2, 25 will be 50. LCM of 12, 18 will be 36. The second trick to remember is that LCM is always a multiple of the greater number. Like in 2, 25, LCM is 50 (25 + 25 or 25 x 2). In 12, 18, LCM is 36 (18 + 18 or 18 x 2). You can check any two numbers. Let’s implement this logic. First, we will find a greater number. We will check till the addition of the two numbers.  And we will increment the number by the greater number, not by one. Like here, we are incrementing 18 + 18 + 18 and so on as required. For this we will use the for loop. We will start to check the numbers from the greater number 18. But the next number will not be 19. The next number will be 18 + 18 = 36. And if this number 36 is divisible by the smaller number also, then this number will be our LCM. What Does the code look like?

Why is swapping required? If our second number is greater than the first number, then, we don’t need to write this swapping line. Like 12 and 15. But if it’s 15 and 12 then you can write your for loop as for i in range(a,(a*b)+1,a). But this is a good approach when you know in advance what numbers are used? If numbers are to be entered at runtime, then, you should follow one rule. So, I am making the second number as the largest number. If my first number is largest, then, I’ll swap it and make the second number largest. Also, you can now understand that in get_lcm (a, b), a, b are parameters, while in get_lcm (x,y), x, y are arguments.

Whatever you can write with a function, you can also without a function. So, what is the use of making a function? Let’s write another program to find HCF of two numbers. Again, as per mathematics, HCF or GCD of two numbers is the highest number other than 1, which can divide both the numbers. For example, 12 and 15 both are divisible by 3. Hence, the HCF will be 3. Like in LCM we need to check the greater number, in HCF, we need to check the smaller number. There are numerous ways. Let’s try three different ways to find the HCF.

This is the most general way to find HCF as we were discussing. And it’s without using any function. Let’s code it down using function as well.

You can see the difference. Everything we write without using function can be written with function as well. The differences are using def function, returning instead of printing, and printing the function name instead of returned value. We can also use the for loop.

We are using min () function, because we have to check only till the smaller of two values. In 12 and 15, we have to check if a number i can divide 12 and 15. We don’t need to check for 13, 14, 15 and higher numbers. But how is it checked? The factors of 12 are 12 = 1, 2, 3, 4, 6, 12 and 15 are 15 = 1, 3, 5, 15. It’ll check 1, then, 2, then 3 and so on till 12 (minimum value). Because 15 can divide 15 but it can’t divide 12, so what is the use of checking 15? So, the HCF is 3. It’s the Highest Common Factor which can divide 12 and 15. What if we can get 3 as our first answer? We do not have to traverse the entire loop. If we check from last number, then, the first value which will divide the both numbers will be our HCF. It means that we have to decrement the loop.

The same can be written using the for loop.

One final method is the popular Euclidean method. The algorithm says that, if we subtract the smaller number from larger (we reduce larger number), HCF doesn’t change. If we keep subtracting repeatedly the larger of two, we end up with HCF. Instead of subtraction, if we divide the smaller number, the algorithm stops when we find remainder 0.

The Euclidean method is also used to find LCM and HCF. The formula is: [ LCM (a, b) = (a * b)/ HCF (a, b) ]. Let’s code this down using a function.

Here, you can see that, once the HCF function has been created, we are using it in finding out the LCM. This is the major use of making a function. If we haven’t used this function, then we had to write the entire HCF program again in the equation.

We can also write a function and do nothing. For this we use the pass function.

It will return a None value. But without this pass (), it’ll throw an error.

Function Arguments in Python

There are four types of arguments that Python UDFs can take:

  • Default arguments
  • Required arguments
  • Keyword arguments
  • Variable number of arguments

Default Arguments

Default arguments as the name suggests are those that take a default value if no argument value is passed during the function call. Default value is assigned by using the assignment operator = sign.

Required Arguments

Required arguments as the name suggests are those that have to be in there. These arguments need to be passed during the function call and in precisely the right order.

Keyword Arguments

These are used to call all the parameters in the right order. Keyword Arguments are used to identify the arguments by their parameter name. 

Note that by using the keyword arguments, you can also switch around the order of the parameters and still get the same result when you execute your function:

Variable Number of Arguments

When the exact numbers of arguments that are to be pass to a function are not known, then this function is written with *, example, *a or *b or any variable name. You’ll found the parameter name *args most widely used. If you remember the tuple packing and unpacking section, if we use *, we can assign any number of values to it. The same thing happens here. The data type is also tuple type. Let’s see how it’s written.

Without this * sign, we could have only passed one argument. Any argument written after * sign, has to be passed with keyword or an error will occur. 

The error says that the function does not take any argument, but we have passed one argument. This error can be removed by passing the argument with a keyword.

Another important concept is using ** sign. This will create a dictionary for the assigned parameter. A dictionary has a key: value pair, thus we have to pass our inputs in such a manner while printing.

We can also pass tuples and dictionaries together. The overall result will be a tuple.

It’s also written as def function_name (args, kwargs), args for arguments and kwargs for keyword arguments. You can use any variable name like def function_name (*t, **d). 

When single data has been assigned and called by its keyword, then, * is used.

When multiple data has been assigned and called by their keywords, then, ** is used. This is also used in dictionaries. Like, *, prefixing ** can unpack all key- value pairs to the function arguments. 

Global vs. Local Variables

The variables that are defined inside a function body have a local scope, and those defined outside have a global scope. That means that local variables are defined within a function block and can only be accessed inside that function, while global variables can be obtained by all functions that might be in your script:

You’ll get a NameError that says that the name ‘m’ is not defined. This is because m is having a local scope. It has its effect as long as it is inside the functional body. By declaring it above the function, we can make it global.

We can also use a global keyword.

If we already have assigned the value of m outside the function, do we still need to write it using the global keyword. 

It’s working. So when should we use this keyword? What will be the output if we have the same variable inside and outside of a function with different values?

It will print both the values, but the preference will be given to the local variable which is assigned inside the function. Let’s remove this local variable m and see what happens?

OK. No errors. So we can access the globally declared variable from inside the function as well. When we want to use the local variable as global variable, no matter what the value of that variable is outside the function, then we use the global keyword.

Can we have both global and local variables? Of course, we can have. Let me put it more clearly. Can we have the same variable as local and global? Of course, there can be situations in which you’ll need to have a variable assigned as both global and local. In that case, using the keyword global will not help you. Python provides another function called globals () to handle such situations. Let’s see how to use it.

Observe the output. If you want to change the value of the global variable, then it is done as follows.

Now the value of the global variable has changed. This is all about global and local variables. Other than these two scopes, we have two more scopes, built-in scopes and enclosing scopes. When Python looks for the given variable, it first looks in the local scope, then enclosing scope, then global scope and then built-in scope. Hence, the priority rule is LEGB rule. Let’s take these two scopes as well. Built-in scopes are for in- built functions. We have to first import this function. Let’s take an example of finding the minimum value from a list. For this we will use min () function. 

It’s simple. Now let’s use a function whose name is min and we are doing nothing but just passing it. 

When we run this, look at the error. It says that min expects 0 arguments and we have passed one. It tried to search for the min function locally, then globally and when it didn’t find it, it gave an error. This happened in spite of the fact that min () is a built-in function. It also made my Jupyter notebook run abruptly. So before restarting it, let me change the function name and re-run the program using Python 3.8. 

Now, it’s working correctly. Another scope is the enclosing scope. The enclosing scope works with nested functions. Let we have an outer function with a local variable x, and an inner function with another local variable x. For simplicity, let’s name it outer x and inner x respectively. Then we will print the inner local variable and then we will print our outer local variable. Let’s code it first.

Ok. So what does it do? It searched for the variable x first in the outer function. There it finds the local variable x. Then it searched the inner function. Also, the inner local variable was present. So, as per LEGB rule, it first prints the inner variable and then the outer variable. Now, let’s comment this inner local variable x. 

What it does is that, it first checked if there is any local variable present. Since we commented on it, it then checks if any enclosing function has the local variable x, and it finds it in the outer function. This is the enclosing scope. Let’s do the vice versa. Now we will comment the outer function local variable and un-comment the inner function and re-run the code.

Oops!! An error occurred. The function checks for x in the inner loop and prints it. Then it checks for the outside local variable and enclosing function and it didn’t find any. Then it checks for global and built-in and when it was not there also, then it throws an error. 

Another important thing is that, with enclosing functions, using global can lead to undesired results. For example, if we want to change the outer variable x. Using global will change the value globally. In this case we use the nonlocal keyword. 

Nonlocal first prints the inner function local variable x and then changed the outer function by overwriting. This approach is more widely used instead of using global when we have enclosing functions and we want to change the outer local variable scope. 

We have seen that an inner function has access to its own function scope (‘local’ variables defined between brackets), but does it have access to the outer function variables as well? Let’s check this first.

We have a function which adds two numbers and then returns the result of two numbers. Let me put one variable outside this function and see if it’s still running.

It’s running. Now let’s make this function inside another function and we will now assign these values as parameters to these functions, the outer value to outer function and the inner value to the inner function.

OK. Let me give some value. Now the tricky question is one value is in inner function and the other value is parameter of the outer function. Can it access the outer variable as well?

Let’s see this. First, assign some values. How to assign? The outer function will be assigned some value say, 3 like this: add_to_func(3). Then this function is assigned to the inner function add_fun. And then we will print add_fun having its own value, say 4. We are doing this because we are returning the inner function because the addition is performed by the inner function.

It means that our inner function variable can access the outer function variables. This phenomenon is called Closure. Closure functions are functions with Preserved Data. In simple words, a closure is an inner function that remembers and has access to the variables in the local scope in which it was created even after the outer function has finished executing. Let’s see one more example.

I have an inner function which will print a message and then return the inner function. But the message is written in the outer function. Finally, the outer function is printed. Let’s code it down.

What is it doing? When the outer function is executed, it goes to the def outer function and checks for the message. Then it goes to the inner function and then it prints the message by returning the inner function. Now remove the parentheses of this returning inner function, so it will execute but it will not return anything. Let me assign this outer function to some other variable and now execute it.

Now my newly assigned variable is equal to the inner function because the outer function is equal to the inner function. We can verify this by using __name__ function, and we can see that this outer function is now assigned to the inner function.

We can see that it’s now an inner function. This new function can behave as a function and if we print this in a few rows, it’ll give output for all. The interesting part is that our outer function has stopped executing but my inner function has still access to the message in outer function. 

This is all about closures.

In the next article we’ll see Decorators.

Design a site like this with WordPress.com
Get started