All You Need to Know About Closures in Python.
Tags: python, programming, computerscience
Published: 2021-10-28 06:35:59

## Article Agenda
In this article, I will explain you the concept of **Closures in Python**. More specifically, we will explore the following:
- **[Nested Functions](#nested-functions)**
- **[Closure Property](#closure-property)**

## 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](https://dev.to/p0intman/what-are-first-class-functions-in-python-213k) or if you are more of an **observational learner**, then you may go ahead and watch the [video version](https://www.youtube.com/watch?v=EBtjFddFvSI) of First Class Function as well.

###### Psst.. If you are in a hurry, you can watch the [video version](https://www.youtube.com/watch?v=bWpm80GzY6w) of this post instead

Alright, let's get started.

## Nested Functions
![nested functions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3t2fh3bbtrjnw35olbch.png)
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:

```python
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:

```python
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vuhs41yyuluqchkd0q7t.png)
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:

```python
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:

```python
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:

```python
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:
```python
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.

```python
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](https://www.youtube.com/channel/UC1QZPervOHLiC4xpVnzbDFg): Home to all sorts of **peculiar** tutos.
- [GitHub](https://www.github.com/P0intMaN): Looking forward to your PR 😉