In this article, you will learn about Python closures, understand the logic behind closures, how to create closures and their significance in programming.
Nested function in Python |
Non-local variables |
Closures in Python: Introduction and Explanation |
Why use closures? |
Before diving into Python closures, there are few concepts one must be familiar with:
So let’s learn about nested functions and non-local variables first.
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()
Output
Hello
Notice in above example, there is no local variable msg
inside 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.
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 'Hello'
.
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.
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
[adsense1]
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: