Article Agenda
In this article, I will explain you the concept of Closures in Python. More specifically, we will explore the following:
Introduction
Now, there's a caveat to this post and that is: You must have a knowledge of First Class Functions. If you don't know about this, then no need to worry. I got your back. Just view my article on First Class Function or if you are more of an observational learner, then you may go ahead and watch the video version of First Class Function as well.
Psst.. If you are in a hurry, you can watch the video version of this post instead
Alright, let's get started.
Nested Functions
Before we understand Closures, it is really important to know about nested functions. Essentially, when you define one function inside another function, the resultant is called Nested function. The following code snippet shows a nested function:
def calculation(number):
def double(no):
return no*2
return double(number)
calculation(10) # 20
In the above code, I have a function calculation()
which takes in a number
as argument. Inside it, I have declared another function double()
which also takes in an argument no
and then returns the double of no
. Finally, I exited from the calculation()
function by making a call to the double()
function.
Now the above scenario was a classical example of a nested function. Let's understand each step briefly:
- A call is made to
calculation()
with parameter value10
. - Interpreter jumps inside
calculation()
and finds areturn
statement with call to another functiondouble()
with the same parameternumber = 10
. Current state is paused. - Interpreter jumps inside
double()
with parameterno = 10
and then returns the double ofno
which is20
. Exits thedouble()
function. -
20
gets reflected and the paused state is resumed. This results in us finally getting the value20
.
Great! See, how easy it was? Okay, lets cover a scenario where you have multiple nested functions inside a parent function. In this case, all those functions will be executed whose call have been made in the parent function's return
statement. The following example demonstrates it:
def calculation(number):
def double(no): # executed
return no*2
def triple(no): # skipped
return no*3
def quadruple(no): # executed
return no*4
return double(number), quadruple(number)
calculation(10) # (20, 40) <-- this is a tuple
Very well. Now this was all about Nested Functions. Now, what if I tell you that there is a neater way of writing the nested functions? There exists a more professional and a cleaner way of writing nested functions and that way makes use of a property called Closure.
Closure Property
Closure property (simply closure) by definition, are function objects that can remember values in its enclosing scopes and these values can be used within that function. I know this might've gone way over your head, so let's understand it bit more clearly.
First of all, let's see what an enclosing scope means:
def parent():
def child()
pass
pass
In the above code, parent
is the enclosing scope for child
. Which means that child
will only be invoked if parent
makes a call for it. Otherwise, child
will stay dormant.
Now, let's see what does "values inside the enclosing scope" mean:
outside1 = "Outside parent func"
def parent(msg):
inside1 = "I am inside parent"
def child()
pass
inside2 = "I am inside parent"
pass
outside2 = "Outside parent func"
parent("Hello there!")
In the above code, outside1
and outside2
are not inside parent()
therefore, they are not inside the enclosing scope of child()
. On the other hand, inside1
and inside2
are inside parent()
function, this means that they are within the enclosing scope of child()
.
Similarly, can you find one more value (or variable) which is inside the enclosing scope of child()
? Yes, the msg
variable. Since we can use msg
inside the parent()
function, we can successfully call it to be in the enclosing scope of child()
.
Finally, let's understand what does "remembers the value in enclosing scope" mean. Let's take our previous example and move on from there:
def calculation(number):
number2 = 30
def double(no):
return no*2
number3 = 50
return double(number)
calculation(10) # 20
I have added two extra variables as well, for better understanding. Okay, so here, as per our previous observations, we can quickly conclude that, number
, number2
, number3
are in the enclosing scope of double()
. Now, let me remind you, closure property states that:
function objects can remember values in its enclosing scopes and these values can be used within that function.
This means that all these variables in the enclosing scope of double()
can easily be accessed inside double and be used in any way you want without even passing them as the parameters to double()
function. Didn't get this? Check out the valid snippet below:
def calculation(number):
number2 = 30
def double():
print(f"I can remember: {number},{number2},{number3}")
number3 = 50
return double()
calculation(10) # I can remember: 10,30,50
Notice that how double()
can access and modify number
, number2
, number3
without them being passed as arguments. Well, this is the due to it being able to "remember" the values in its enclosing scope. This was the great story of Closure.
Now, before ending this article, lets refactor our previous cluttered code that we wrote without closures. You would be seeing a significant difference in the style.
def calculation(number):
def double(): # executed
return number*2
def triple(): # skipped
return number*3
def quadruple(): # executed
return number*4
return double(), quadruple()
calculation(10) # (20, 40) <-- this is a tuple
Looks neat ain't it? No tossing around of parameters and not even explicitly passing them. The code looks way more cleaner and professional.
Would You Like to Support Me?
If you want to support me and my contents, then go ahead and consider doing it. I would highly appreciate that:
- YouTube Channel: Home to all sorts of peculiar tutos.
- GitHub: Looking forward to your PR 😉
Top comments (0)