In this lesson you will learn about Closures in Python.
Knowledge of closures important to understand
decorators, which we will cover in the next lesson.
To understand closures, first we have to learn about
So Let’s start from ground up, starting from functions.
Functions are objects.
As you know, in Python everything is an object,
so functions are also objects.
We can assign them to a variable as like, int, string, etc.
Let’s consider a simple function ‘func’
def func(): print("Simple function")
if we call func() it will print “Simple function”
We can assign it to a variable
f = func
and we call the function by using () with ‘f’ variable
f() # prints 'Simple function'
Functions can also create functions.
Functions within functions
It’s possible to define function inside a function.
A function defined inside another function,
is called a nested function.
Below we have defined two functions inside another function.
def outer_func(): print("I'm in outer_func ") # defining a function inside def inner_one(): print("\tI'm in inner_one ") # defining another function inside def inner_two(): print("\tI'm in inner_two ") # calling first function inner_one() # calling second function inner_two() print("I'm back in outer_func ") outer_func() output: I'm in outer_func I'm in inner_one I'm in inner_two I'm back in outer_func
Notice we have called inner_one() and inner_two()
inside outer_func() block.
can we call inner_one() directly ?
inner_one() output: --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-10-5aae44ee5b62> in <module>() ----> 1 inner_one() NameError: name 'inner_one' is not defined
We get NameError, because inner_one is not in globals.
Functions defined inside a function,
are also called local functions,
because they are local to outer function.
Now you understand how a function creates another function.
It’s time to understand the variable scope,
with respect to functions.
Variables are evaluated by simple scoping rules.
Usually known as LEGB rules.
LEGB is an abbreviation for
Local, Enclosing, Global Built-in.
Variables are evaluated from inner to outer scopes,
A variable definition lookup starts from local scope,
if it is not found, then look in enclosing scope, and so on.
The order of the look up is
Local -> Enclosing -> Global( module scope) -> Built-in
A simple example show the the scoping of variables.
gl = 'global_value' print('Global scope') def outr_func(arg ='arg_value'): print("\tEnclosing scope") en ='enclosed_value' def inner_func(): lc = 'local_value' print("\t\t Local Scope") print(lc,en,arg,gl) print('\tBack to enclosing scope') inner_func() #print("can't access local here", lc) outr_func() output: Global scope Enclosing scope Back to enclosing scope Local Scope local_value enclosed_value arg_value global_value
Did you notice this line of code,
We are printing lc,en,arg,gl,
these are variables are defined in local,
enclosed, and global scope respectively.
‘lc’ is defined inside inner(),
so it can access its variables, its straight forward.
But how inner() function can access en,gl variables?
It is through LEGB rules.
Definition of ‘el’ is looked up first inner() function,
since its not available, move one level up, i.e enclosing scope.
Similrly for ‘arg’,’gl’ definition lookup.
The lookup continues till built-in scopes.
If Python doesn’t find definition at all even in
built-in scope, it raises ‘NameError’ exception.
You can’t access, inner scope variables in outer scope,
for example, if you try to access ‘lc’ in enclosing scope
again the NameError exceptionis raised.
#partial code shown inner() print("can't access local here", lc) outer() output: NameError: name 'lc' is not defined
What is the use of local functions ?
Local function are useful for specialized,
Local function helps in code organization and readability,
They are like lambda expression, but are more intuitive.
Function as an argument to a function.
Functions are first class objects, that means,
a function can be passed as an argument to a function
and can be returned as value too.
Below we have a simple function,
which is takes a function as argument, and returns a value.
Let’s look at some simple examples.
function takes function as an argument and returns it. def my_len_func(f, x): return f(x) Calling my_len_func() my_len_func(len,'justlearnpython')
We have passed two arguments to my_len_func(),
first is a built-in len() function and
‘justlearnpython’ a string as second argument.
in the code ‘return f(x)’
‘f(x)’ is evaluated to len(‘justlearnpython’)
Another example shows a function defined inside another,
Here in this example we have an outer sort_by_last_letter() function,
and last_letter() is an inner function, which is used as helper function
to sorted() built-in function.
In above examples we have used built-in functions
inside inner function.
Let’s look at few examples of user defined
outer and inner functions.
Another simple example,
def enclosing(): def local_func(): print("Im local func") return local_func # lcl_func assigned with enclosing function object. lcl_func = enclosing() # lcl_func reference is called as function. lcl_func() output: I'm local func
A local_func() is defined and returned from a enclosing() function.
Notice how the function is returned, ‘return local_func’
the ‘( )’ are not used, local_func is a reference which being returned.
What is a clousre?
A closure is just function which is defined inside another
function, which uses values from enclosing scope.
Closure function remembers values of
in enclosing scopes, even if they are not present in memory.
Which is, even after outer function completes execution,
the inner function remembers, references of outer function values.
It’s bit confusing right?, let’s look at an example.
Closure example, A function returned from another function.
def outer(): x = 10 def inner(y): return x + y return inner
In above example outer() function returns inner()
function. The inner function takes one argument ‘y’,
it returns sum of ‘x’ and ‘y’,
where ‘x’ a variable in enclosed scope.
Now let’s call inner and outer functions.
clousre1 = outer() a = clousre1(20) print(a) output: 30
When we call outer() function,
clousre1 = outer()
outer() returns reference to inner() function,
which is assigned to clousre1 variable.
Calling inner() function.
inner() function is called with ( ) brackets on clousre1 variable.
a = clousre1(20) # y is 20 'a' has value 30.
Note inner() function uses value of ‘x’,
inside it’s scope and returns ‘x + y’.
How did inner() function access ‘x’ even though
outer() function already finished its execution.
at clousre1 = outer(),
That’s because, inner() function remembers reference to ‘x’,
and it access ‘x’ with this reference,
when it is called at,
a = clousre1(20) # y is 20
Let’s look at two more simple examples of closure.
Inner function uses outer function variable.
This example is same as above, the only difference
here is outer takes an argument ‘n’
def outer(n): def inner(x): return n + x return inner clousre1 = outer(10) clousre2 = outer(100) a = clousre1(1) print(a) b = clousre2(1) print(b output: 11 101
In this example, outer() function, ‘n’ as argument.
inner() function takes ‘x’ as argument,
it returns the sum of ‘n’ and ‘x’
We have two variables to inner() functions, closure1 and clousre2,
which when called returns 11, 101 values respectively.
Python implements closure with a special method __cloure__
We can examine the closure by accessing this method.
clousre1.__closure__ output: cell at 0x000001BCF0F73408: int object at 0x0000000073EE61C0>
This shows closure1 referring to a single object,
i’e ‘n’ from the above example.
suppose we have another variable in enclosing scope ‘y’
def outer(n): y = 20 def inner(x): return n + x + y return inner clousre1 = outer(10) a = clousre1(1) print(a) output: 31
Examine the __closure__ on closure1
closure1.__closure__ output: (<cell at 0x000001BCF0F73E58: int object at 0x0000000073EE61C0>, <cell at 0x000001BCF0F73BE8: int object at 0x0000000073EE6300>)
The output shows closure1 is referring to two objects,
‘n’, ‘y’ from enclosing scope.
Closures are commonly used to create factory functions.
Factory functions return specialized function, which does some special
operation on enclosed values.
In this example, raise_to_power() is a factory function,
which returns, special function raise_to_y()
def raise_to_power(y): def raise_to_y(x): return pow(x,y) return raise_to_y to_sqare = raise_to_power(2) to_cube = raise_to_power(3) to_sqrt = raise_to_power(0.5) to_fourth = raise_to_power(4) calling raise_to_y() to_sqare(2) output: 4 to_cube(2) output: 8 to_fourth(2) output: 16 to_sqrt(2) output: 1.4142135623730951
here to_sqare, to_cube, to_fourth, to_sqrt all are closures.
We can cross check this by
to_sqare.__closure__ output: (<cell at 0x000001BCF0F733D8: int object at 0x0000000073EE60C0>,)
We are almost done with closure, but with concept ‘nonlocal’
What is nonlocal ?
x = 1 def outer(): x = 10 def inner(): x = 20 print("inner : x = ",x) print("enclosing_1 : x =",x) inner() print("enclosing_2 : x =",x) print('global_1 : x =',x) outer() print('global_2 : x =',x) output: global_1 : x = 1 enclosing_1 : x = 10 inner : x = 20 enclosing_2 : x = 10 global_2 : x = 1
Closely observe the output.
Why value of ‘x’ is did not change in outer() or inner() function.
Its because all variables are local in their scope.
If you want to modify a value of a enclosed or global variable
use ‘nonlocal’ keyword, which is explicitly telling Python
don’t treat it as local in current scope.
Let’s the use of nonlocal
def outer(): x = 10 def inner(): nonlocal x x = 20 print("inner : x = ",x) print("enclosing_1 : x =",x) inner() print("enclosing_2 : x =",x) print('global_1 : x =',x) outer() print('global_2 : x =',x) output: global_1 : x = 1 enclosing_1 : x = 10 inner : x = 20 enclosing_2 : x = 20 global_2 : x = 1
Now you see, we have modified our code a bit
in inner() function, we have explicitly said
before assigning a new value to x.
x = 20
So when inner() is called, the value of ‘x’ is
changed from 10 -> 20
enclosing_1 : x = 10 inner : x = 20 enclosing_2 : x = 20
In the next lesson you will, learn how closures can be used as Decorators.