Understanding Python Closures in Simple Terms
Closures are one of the most discussed topics of functional programming that offers brilliant solutions for some concerns associated with Object-Oriented Programming (OOP).
Most of the programmers use closures deliberately and sometimes without having a good understanding of how they really work.
Closures in Python (or any other language) not only provide good control on code control but also make the python program simpler and easier.
Now, we shall discuss the significance of python closures, how to use them, and when to use along with simple examples.
However, before diving deep into the concept of closures, you should know the concept of nested functions and non-local variables.
If a function is defined inside another function, then it’s known as a nested function. In general, nested functions mostly access the variables within the enclosing scope. Let’s take an example to understand it clearly.
def outer_func(): msg = 'Hello' def inner_func(): print (msg) inner_func() outer_func()
Did you observe anything strange in the above example? If you carefully look at the example, you find the absence of the local variable “msg” inside the function inner_func(). But still, print the msg variable that is defined in the outer function - outer_func().
When the function didn’t find any local variable, then it usually takes the local variable defined inside the function that is enclosed. This is known as the enclosing scope.
Now, let’s take a look at the following example to understand the non-local variable and its significance.
def outer_func(): msg = 'Hi' def inner_func(): msg = 'Hello' print (msg) inner_func() print (msg) outer_func()
Here is the obtained output.
Now, if you look at the above example, the program called the function outer_func() and surprisingly, the first result shown in the output is the output of the inner function - inner_func() (i.e - “Hello”).
However, when the variable “msg” is printed again, then the obtained result is “Hi”. This is due to the termination of the “inner_func()”. To be more clear, when “inner_func()” is terminated, the variable defined under that function also gets terminated.
Now, let’s consider another example using a non-local variable in the “inner_func()”.
def outer_func(): msg = 'Hi' def inner_func(): nonlocal msg msg = 'Hello' print(msg) inner_func() print (msg) outer_func()
Running the above code prints the results as follows.
Now, if you look at the output, it’s printed “Hello” two times. This is because of the declaration of the variable as non-local within the nested function.
In general, the scope of the variable extends from inner function to outer function when a variable is declared as non-local within the nested function. This is the reason the “msg” variable within the inner function is bound to the outer function.
This is all about the nested functions and non-local variables. Now, it’s time to dwell on the actual concept of python closures.
Python Closure Examples
In simpler words, closure is nothing but the process of binding the data to a function. Most of the beginners misunderstand the word “closure” with the inner function. In fact, inner function causes closures.
Closure contains two environments: the inner and outer, and has access to the variables from both the inner and outer environments.
Consider the below-given simple example to understand the closure concept.
def print_message(msg): msg = "Hi Outer" def print_now(): print(msg) return print_now p_message = print_message("Hello Inner") p_message() p_message()
In this example, the “print_message()” function creates and returns a function. While the nested print_now() function is closure. The print_message() function returns to the print_now() function and binds with it. That’s it, the execution is done. And the output obtained is as follows.
Hi Outer Hi Outer
In this example, let’s consider the non-local variable. Usually, the non-local variable enhances the modification of the inner function with the outer function.
def incremental_counter(): count = 0 def increment(): nonlocal count count += 1 return count return increment incr = incremental_counter() c = incr() print(c) c = incr() print(c) c = incr() print(c)
And the resultant output is:
1 2 3
When the above program is executed the function incremental_counter() returns another function called increment(). This inner increment() function has access to the count variable from the outer function even after the incremental_counter() function has completed its execution. The count variable here denotes the part of closure and that is used by the increment() function even after the outer function is finished up.
Note: In Python, the use of nonlocal keywords enables closures to change variables in their outer environment.
When You Should Use Python Closures
Closures are not used everywhere. It also has some limitations. To avoid confusion on when to use the closure, here is a list of conditions on when to use them.
- Used only in case of nested function. If there is no nested function, then you are not allowed to use closures.
- Use closure when the nested function refers to a variable inside the enclosing function.
- Used when the enclosing function returns the nested function.
Why You Should Use Python Closures
Closures make programming easier and simpler. Usually, closures reduce the use of global variables and hide the data. These closures even replace hard-coded constants. This is even an efficient way of programming when you have few functions in your code.
In a nutshell, python closures provide a simple and alternate solution for complex functions in a class.