DEV Community π©βπ»π¨βπ» is a community of 963,274 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

P0intMaN

Posted on

All You Need to Know About Closures in Python.

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:

1. A call is made to `calculation()` with parameter value `10`.
2. Interpreter jumps inside `calculation()` and finds a `return` statement with call to another function `double()` with the same parameter `number = 10`. Current state is paused.
3. Interpreter jumps inside `double()` with parameter `no = 10` and then returns the double of `no` which is `20`. Exits the `double()` function.
4. `20` gets reflected and the paused state is resumed. This results in us finally getting the value `20`.

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

return no*4

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

return number*4

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: