Python Nested Functions and Closures

Before reading this article, I recommend to read the below two articles to understand better.

Nested Functions

  • Nested functions are functions defined within other functions.
  • Nested functions can read variables declared in the immeditely outside function
>>> x = 10
>>> def outer():
...     y = 20
...     def inner():
...             x = 30
...             y = 40
...             z = 50
...             print("X ="+ str(x))
...             print("Y ="+ str(y))
...             print("Z ="+ str(z))
...     inner()
...     print("Y =" + str(y))
...
>>> outer()
X =30
Y =40
Z =50
Y =20
>>> x
10

Nested Function using Global and Non Local

>>> x = 10
>>> def outer():
...     y = 20
...     def inner():
...             global x
...             x = 30
...             nonlocal y
...             y = 40
...             z = 50
...             print(f"x = {x}, y = {y}, z = {z}")
...     inner()
...     print(f"y ={y}")
...
>>> outer()
x = 30, y = 40, z = 50
y =40
>>>
>>> def outer(num):
...     def inner():
...             print(num)
...     inner()
...
>>> outer(10)
10

Closure Function.

A closure is a nested function with an after-return access to the data of the outer function, where the nested function is returned by the outer function as a function object.

  • We should have nested function.
  • Nested function has to refer to a variable defined inside the enclosing function.
  • Enclosing function must return the nested function.
>>> def outer(num):
...     def inner():
...             print(num)
...     return inner
...
>>> myfun = outer(999)
>>> myfun()
999
>>> num = 1010101
>>> myfun()
999
>>> del outer
>>> myfun()
999

>>> def outer(num1):
...     def inner(num2):
...             return num1 * num2
...     return inner
...
>>> myfun = outer(10)
>>> myfun(20)
200

>>> def factouter():
...     def factinner(n):
...             if n==1:
...                     return 1
...             else:
...                     return n * factinner(n-1)
...     return factinner
...
>>> myfact = factouter()
>>> myfact(10)
3628800
>>> myfact(5)
120

Using Non Local variables inside Inner Function for the variables defined in the enclosing scope

>>> def outer(x):
...     y = 10
...     def inner(z):
...             nonlocal y
...             return x*y*z
...     return inner
...
>>> myfun = outer(8)
>>> myfun(5)
400

Passing Functions to Closure Function

>>> def outer(funobj):
...     def inner(str):
...             funobj(str)
...     return inner
...
>>> def display(name):
...     print(name)
...
>>> myfun = outer(display)
>>> myfun("Girish")
Girish

In the below example, I have created a Multiplication Table Printing Function which takes table number and maximum multiplier number as input. The function object is passed to the closure function.

>>> def mul(table, n):
...     for x in range(1,n+1):
...             print(f" {table} x {x} = {x*table}")
...

>>> def outer(funobj):
...     def inner(table,max):
...             funobj(table,max)
...     return inner
...

>>> myfun = outer(mul)
>>> myfun(9,15)
 9 x 1 = 9
 9 x 2 = 18
 9 x 3 = 27
 9 x 4 = 36
 9 x 5 = 45
 9 x 6 = 54
 9 x 7 = 63
 9 x 8 = 72
 9 x 9 = 81
 9 x 10 = 90
 9 x 11 = 99
 9 x 12 = 108
 9 x 13 = 117
 9 x 14 = 126
 9 x 15 = 135

References

  • https://docs.python.org/3/faq/programming.html
  • https://www.python.org/dev/peps/pep-0227/
  • https://www.python.org/dev/peps/pep-3104/#new-syntax-in-the-referring-inner-scope

Learn more about Python features in our upcoming blog articles.

Happy Learning!