In this article, you will learn about Python closures, understand the logic behind closures, how to create closures and their significance in programming.
Before diving into Python closures, there are few concepts one must be familiar with:
- Nested functions
- Non-local variables in Python.
So let’s learn about nested functions and non-local variables first.
Nested function in Python
A function defined inside another function is simply called a nested function.
Let’s take an example of nested function and illustrate the scope of non-local variables.
def function_outside(): msg = 'Hello' def function_inside(): print (msg) function_inside() function_outside()
Notice in above example, there is no local variable
function_inside(), still, it prints the
msg defined outside that function.
That is because when a function doesn’t find a local variable it looks up for a local variable defined inside the function it is enclosed or nested within. This is called the Enclosing scope.
Non-local variable in Python
Let’s consider following example to gain insight on non-local variable and its importance.
def function_outside(): msg = 'Hi' def function_inside(): msg = 'Hello' print (msg) function_inside() print (msg)
Now let’s try running this code in the interpreter.
>>> function_outside() Hello Hi
As you can see in above example when the
function_outside() is called, first the
function_inside() is invoked printing out the msg variable holding the value
After that when we print
msg again, it prints the value
'Hi'. That is because as soon as
function_inside() terminates, the variable defined inside it is also destroyed. So the variable local to outer function is printed.
Now let’s see what happens when we use a non-local variable in function_inside( ).
def function_outside(): msg = 'Hi' def function_inside(): nonlocal msg msg = 'Hello' print (msg) function_inside() print (msg)
Now let’s try running this code in the interpreter.
>>> function_outside() Hello Hello
What just happened?
Well, when we declare a variable inside a nested function as nonlocal, its scope is extended beyond this inner-function to the outer-function it is nested within. Hence, the
msg variable inside the inner function is bound to the msg variable in outer function overriding its value.
Now that we know about nested functions and non-local variables, let’s learn in depth about Python closures.
Python Closures: Introduction
Basically, the method of binding data to a function without actually passing them as parameters is called closure. It is a function object that remembers values in enclosing scopes even if they are not present in memory.
What do I mean by binding data to a function without actually passing them as parameters?
Let’s take an example to simplify it.
def func1(): #Outer function msg = 'I belong to func1' def func2(): #Nested function print (msg) return func2
In previous examples, we just called the nested function inside a function. Here we have returned the nested function instead of just calling it. This way we can return the whole functionality of nested function and bind it to a variable to use it further.
Let’s run this program in an interpreter and see what actually happens.
>>> obj = func1() #binding the function to an object >>> obj() I belong to func1
Here is what’s happening in the program.
But this is nothing new to what we saw in Nested functions, Where does closure come into play?
Well when the interpreter detects the dependency of inner nested function on the outer function, it stores or makes sure that the variables in which inner function depends on are available even if the outer function goes away.
Technically the variable msg should have vanished away with outer function but s you can see in the picture, the variable msg in which the inner function depends on is bound to that function, even if the outer function goes away.
Hence, this method of binding data to a function without actually passing them as parameters is called closure. It is a function object obj that remembers values in enclosing scopes even if they are not present in memory.
Try the following code in an interpreter to see actual results.
>>> obj = func1() #binding the function to an object >>> del func1 #deleting the outer function >>> func1() #this returns error as the function is deleted Traceback (most recent call last): func1() NameError: name 'func1' is not defined >>> obj() I belong to func1
As you can see in above example, even when the outer function is deleted the object still stores and binds the variable msg to inner nested function. This is called closure in Python.
In conclusion here are the three criteria’s for a closure:
- There must be a nested function (a function inside another function).
- This nested function has to refer to a variable defined inside the enclosing function.
- The enclosing function must return the nested function.
Why should we use closures?
- Closures provide some sort of data hiding as they are used as callback functions. This helps us to reduce the use of global variables.
- Useful for replacing hard-coded constants
- Closures prove to be efficient way when we have few functions in our code.