Home Python Closures
Post
Cancel

Python Closures

Nested Function

When a function is defined inside another function, then it is called Nested Funciton. The Nested Function can access the variables of the enclosing scope. In Python, these non-ocal variables are read-only be default and we must declare them explicitely as non-local in order to modify them. Example:

1
2
3
4
5
6
7
8
9
10
11
    def print_msg(msg):
        # This is the outer enclosing function

        def printer():
            # This is the nested function
            print(msg)
        printer()

    print_msg('Hello')
    
    Output: 'Hello'

Here, we can see that the printer() function can access the non-local msg variable of the enclosing function.

Defining a Closure Function

In the example above, what would happen if the last line of the function print_msg() returned the printer() function instead of calling it? This means the function was defined as follows:

1
2
3
4
5
6
7
8
9
10
11
12
    def print_msg(msg):
        # This is the outer enclosing function

        def printer():
            # This is the nested function
            print(msg)
        printer()

    another = print_msg('Hello')
    another()

    Output: 'Hello'

Here, we can see that the print_msg() function was called with the string ‘Hello’ and the returned function was bounded to the name another. And on calling another(), the message was still remembered although we had already finished executing the print_msg() function.

This technique by which some data gets attached to the code is called Closure In Python.

This value in the enclosing scope is remembered even when the varibale goes out of scope or the function itself is removed from the current namespace.

1
2
3
4
5
6
7
8
9
    del print_msg
    anoter()

    print_msg('Hello')

    Output:
    Traceback (most recent call last):
    ...
    NameError: name 'print_msg' is not defined

Here, the returned function still works even when the originalf function was deleted.

Criteria For Closures

Following are the criteria that must be met when defininig a Closure in Python:-

  • We must have a nested function
  • The nested function must refer to a value defined in the enclosing function.
  • The enclosing function must return the nested function.

When do we use closures?

Closures offer some sort of data concealment and can be substitutes for the use of global variables. Additionally, it can offer an object-oriented solution to the issue.

Closures can offer an alternative and more elegant way when there are only a few methods (one method in most situations) to be implemented in a class. However, it is preferable to implement a class as the number of characteristics and methods increases.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    def make_multiplier_of(n):
        def multiplier(x):
            return x * n
        return multiplier

    # Multiplier of 3
    times3 = make_multiplier_of(3)

    # Multiplier of 5
    times5 = make_multiplier_of(5)

    print(times(9))
    Output: 27

    print(times5(9))
    Output: 45

    print(timest(times(2)))
    Output: 30

Note: Python Decorators make an extensive use of closures as well.

Finally, it is important to notice that it is possible to determine the values that are encompassed in the closure function.

All function objects have a __closure__ attribute that returns a tuple of cell objects if it is a closure function. We can identify times3 and times5 as closure functions by using the aforementioned example.

1
2
3
4
5
    make_multiplier_of.__closure__
    times3.__closure__

    Output: 
    (<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)

The cell objects has the attribute cell_contents which stores the closed value.

1
2
3
4
5
    times3.__closure__[0].cell_contents
    Output: 3

    times5.__closure__[0].cell_contents
    Output: 5
This post is licensed under CC BY 4.0 by the author.