Saturday, January 9, 2016

Python Closures

Python does provide another useful feature called Closures. A Closure is a function object that remembers values in the enclosing scopes regardless of whether those scopes are still present in memory. Closures are functions that inherit variables from their enclosing environment. If you have ever written a function that returned another function, you probably may have used closures even without knowing about them.

Consider a sample Python code,

def print_hello(msg):
        def printt(var):
            print msg * var
        printt(4)

print_hello("hello")

Once you execute the code we can see,
[djas999@vx181d imp]$ python hello.py
Hellohellohellohello

Python provides some additional facilities that most other high level languages do not provide. One such facility is to assign a function to a variable. The same above snippet can be written as

[djas999@vx181d imp]$ cat hello.py

def print_hello(msg):
        def printt(var):
            print msg * var
        return printt

hello=print_hello("hello")
hello(5)


In the above code we have returned the print(var) method and we created a variable hello which now contains the print_hell(“hello”) method. Then we can invoke the function by calling the variable like hello(5).

The print_hello() function was called with hello and retuned function was bounded to another name called "hello" . Now the closures come in action. Even though the print_hello(“hello”) method execution is completed and removed from scope,on calling hello(), the message was still remembered .This technique by which some data ("Hello") gets attached to the code is called closure in Python.

The main point is that the value from the top function is remembered in the sub function even though the scope of the function is complete or the function is removed from the namespace. This is called Closure.

Function attributes are available in func_closure in python < 3.X and __closure__ in python > 3.X save the free variables that are being passed from one scope to another.

Now if we add the func_closure code, we can see
[djas999@vx181d imp]$ cat hello.py

def print_hello(msg):
        def printt(var):
            print msg * var

        return printt

hello=print_hello("hello")
hello(5)
print hello.func_closure

[djas999@vx181d imp]$ python hello.py
hellohellohellohellohello
['__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']

 [djas999@vx181d imp]$ cat hello.py

import os
def print_hello(msg):
        def printt(var):
            print msg * var
        return printt

hello=print_hello("hello")
hello(5)
print dir(hello.func_closure[0])
print hello.func_closure[0].cell_contents

The above snippet also gives us the value of the variable as “hello” if we run the code.

Not closure

def make_printer(msg):
 ...     def printer():
 ...         pass
 ...     return printer
 ...
 >>> printer = make_printer('Foo!')
 >>> printer.func_closure


If a function does not use free variables it doesn't form a closure.

If there is another inner level which uses free variables -- all previous levels save the lexical environment. 

No comments :

Post a Comment