Python Closure

Filed Under: Python

To understand python closure, you should have the idea of nested function and python class. Actually python closure is also a function that provides the opportunity to encapsulate some data with code.

Python nested function


def funcOut():
    print("Now we are in funcOut.")
    def funcIn():
        print("This function is defined inside the funcOut.\nThis one is called a nested Function.")
    print("Here we will call the funcIn that is defined.")
    funcIn()
print("We are in _main_.\nCalling the funcOut.")
funcOut()

In the above code funcIn is nested function inside the funcOut. If you look at the output of the above code then you will understand the calling sequence of the functions. The output will be:


We are in _main_.
Calling the funcOut.
Now we are in funcOut.
Here we will call the funcIn that is defined.
This function is defined inside the funcOut.
This one is called a nested Function.

Turning the funcOut into a python closure

Say, you want to have all the functionalities that is done by the funcIn from funcOut.

How can you do this? What comes in your mind?

return right!!!

Normally we return a value or reference form a function. But here, we need to return the whole functionalities of funcIn. If we just overwrite the function calling funcIn() in line 6 by return funcIn, then we have achieved what we want.

The thing that we have just done is known as closure in python. You will understand python closure more clearly as you go through the whole tutorial.

Idea of Python Closure

So, from the above we have learned that when a function returns another function defined in ( ie. nested function) it, it’s called a closure. Let’s now have a look on sample structure of a closure.

Python Closure Structure


def closureFunc():
  def nestedFunc():
     # ... statements ...
      print(" Welcome To Closure ")
  return nestedFunc
get = closureFunc()
get()

This will output:


Welcome To Closure 

In the above code, as per the name of the function I hope you understand the outer function is the closure function, in which there is a nested function which is being returned by the closure function.

Python closure embeds data with code

When we create a object of a class, this object contain some information with it. Just like that closure embeds data with the code.

Let’s explore with an example code


def closureFunc(n):
    def nestedFunc():
        # ... statements ..
        print("Welcome To Closure ")
        print("You have sent argument %d + 10 = %d" % (n, n+10))
    return nestedFunc
getting = closureFunc(12)
getting()

This will output:


Welcome To Closure 
You have sent argument 12 + 10 = 22

Notice line 7 and 8 – getting variable is now working as a function. All the functionalities of the inner function of nested function is now being done by it.

Python Closure remembers its context

Look at the following code, we have deleted the closureFunc.


def closureFunc(sum):
   def nestedFunc():
      # ... statements ..
       print("Welcome To Closure ")
       print("You have sent argument %s" % sum)
   return nestedFunc
getting = closureFunc(12)
del closureFunc
getting()

This will output:


Welcome To Closure 
You have sent argument 12

This is the power of closure. Even if you delete the closure function the getting remember its context where it was and what it does. That’s why we got the output of getting even after deleting the actual function.

Use of nonlocal variable in closures

Let’s have a look to another example. The following closure adds up all the number upto a certain range which is given as argument to the closure function.


def closureFunc(up):
   val = 0
   def nestedFunc():
       nonlocal val
       print("Welcome To Closure ")
       for i in range(up+1):
           val += i
       print("Total is =  %d" % val)
   return nestedFunc
getting = closureFunc(5)
getting()

This will output:


Welcome To Closure 
Total is =  15

Notice that we have taken a variable val in the closureFunc and reuse it in the nestedFunc declaring as a nonlocal to this function using the keyword nonlocal.

If you do not declare as nonlocal then you will get error that local variable ‘val‘ referenced before assignment, that means it will be considered as a local variable to the nestedFunc function .

Closure with argument

Let’s have a look at the last example of this tutorial. In this code we want to provide argument to the nestedFunc. And observe the output for different value.


def closureFunc(up):
   val = 0
   def nestedFunc(arg):
       nonlocal val
       print("Welcome To Closure ")
       for i in range(up+1):
           val += i
       val *= arg
       print("Total is =  %d" % val)
   return nestedFunc
retFunc = closureFunc(5)
retFunc(10)
retFunc(4)

Below image shows the output of above python closure program.
python closure

If you can understand why the second output is 660 then I must say you have gained knowledge from this tutorial.

The output is 660 because, when line 11 executes, variable up=5 is set.

Then when line 12 executes, nestedFunc executes and variable val=150 is set.

After that when we again call the function with different argument 4 in line 13, then the closureFunc is having up=5, val=150. So in the for loop val is updated by 150 plus summation of 1 to 5 that equals 150+15 = 165. Then multiply it with 4 which equals 660. That’s it. This is what python closure is. Hope this tutorial is helpful for you. Best of luck coding with closure.

__closure__

All function objects have a __closure__ tuple attribute that returns cell objects if it is a closure function.


def closureFunc(up):
    val = 0

    def nestedFunc(arg):
        nonlocal val
        print("Welcome To Closure ")
        for i in range(up + 1):
            val += i
        val *= arg
        print("Total is =  %d" % val)

    return nestedFunc


retFunc = closureFunc(5)
print(retFunc.__closure__)
print(retFunc.__closure__[0].cell_contents)
print(retFunc.__closure__[1].cell_contents)

retFunc(10)
print(retFunc.__closure__)
print(retFunc.__closure__[0].cell_contents)
print(retFunc.__closure__[1].cell_contents)

retFunc(4)
print(retFunc.__closure__)
print(retFunc.__closure__[0].cell_contents)
print(retFunc.__closure__[1].cell_contents)

It will produce following output now and closure context values up and val are also getting printed.


(<cell at 0x10079f288: int object at 0x10028ba80>, <cell at 0x101033618: int object at 0x10028b9e0>)
5
0
Welcome To Closure 
Total is =  150
(<cell at 0x10079f288: int object at 0x10028ba80>, <cell at 0x101033618: int object at 0x10028cca0>)
5
150
Welcome To Closure 
Total is =  660
(<cell at 0x10079f288: int object at 0x10028ba80>, <cell at 0x101033618: int object at 0x1007eae70>)
5
660

Python closure is a good to know feature but it gets complicated if we have more inner functions and arguments. You can achieve the same thing with classes and normal functions. So use python closure with caution.

Reference: StackOverflow question

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages