Closure — Python

Diane Khambu
5 min readJul 26, 2021
Photo by Nick Bolton on Unsplash

I know which side of class I was sitting when closure topic came in Programming Languages class in Junior year of college. Having come across so many details of programming languages from currying to machine instructions, I was not that happy when closure came. I was fine with function and method.

Well, closure is more interesting, cool and also useful than what I might have initially sighed about.

In this article, we’ll go over variable scopes global scope, local scope and nonlocal scope, and how closure works with variables of different scopes to make it interesting.

Global Scope

It is a module scope. It spans a single file only. There is no concept of truly global, available in all modules, scope. The only exception to this are built-in globally available objects, such as True, False , None, print , dict , help etc.

Global scopes are nested inside a built-in scope.

A namespace is a structure that contains unique name for every objects in Python. The objects could be a variable or a method.

You can use globals() built-in function in builtin module to see objects in global space.

The globals() function returns a dictionary containing a current scope’s global variables. We also looked up __builtins__ key from the dictionary to see what all objects it contains.

Local Scope:

When we create functions, we can create variable names inside those functions using assignments. eg count = 0 . These variables are not created until the function is called and are scoped to that each individual function that contains them.

An actual object a variable references can be different for each function call which is also the case in recursion.

Let’s see an example:

On running the file, we get:

local()= {'a': 3, 'b': 'd', 'c': 'ddd'}local()= {'a': 200, 'b': 100, 'c': 20000}

We used locals() built-in function to see local variables inside the multiply function. We called it twice and each time the function’s local is different.

Let’s combine local and global scope in another example:

On running the file, we get:

function count #1's local()= {'a': 3, 'b': 'd', 'c': 'ddd'}function count #2's local()= {'a': 200, 'b': 100, 'c': 20000}

Now let’s visualize the code with scope:

We have variables in all built-in , global , and local scopes. We had to use global keyword to indicate that variable count is of global scope, therefore preventing UnboundLocalError exception.

Nonlocal scope

It is a scope of enclosing function and not declared in a function where it’s being used.

Let’s see an example:

On running the function, we get:

[BEFORE] outer_func's x's value: Harry, id: 140234708990448
inner_func's x's value: Potter, id: 140234709483120
[AFTER] outer_func's x's value: Harry, id: 140234708990448

The value of x in inner_func function is Potter and its id is different than that of outer_func both before and after the function call. When the inner_func is compiled, Python sees an assignment to x . Hence, it determines that x is a local variable to inner_func .

Let’s checkout another example:

On running the file, we get:

[BEFORE] outer_func's x's value: Harry, id: 140307857914352
inner_func's x's value: Potter, id: 140307858406960
[AFTER] outer_func's x's value: Potter, id: 140307858406960

Here we see that outer_func ‘s x ‘s value after inner_func is called is changed to Potter . Inside the inner_func function, we used nonlocal keyword to refer to a variable enclosing it, and modified its value. Hence, it is reflected both in inner_func and after the inner_func function call in the outer_func .

Notice that variable x ‘s id value is same in inner_func and after inner_func function call in outer_func function scope. This is because string is immutable and if we want to update its reference a new object is created and there’s an update in the reference.

This is what’s happening pictographically.

Just for completion, you wouldn’t need to use nonlocal keyword if you want to refer to enclosed scope’s variable that is mutable because reference is still the same.

Here’s an example:

On running the file, we get:

[BEFORE]: dict_keys(['english', 'mandarin', 'french', 'italian', 'swahili', 'german']), id: 140654542806592
[INNER]: dict_keys(['english', 'mandarin', 'french', 'italian', 'swahili', 'german', 'motswana']), id: 140654542806592
[AFTER]: dict_keys(['english', 'mandarin', 'french', 'italian', 'swahili', 'german', 'motswana']), id: 140654542806592

Look, the id ‘s value is same in all before, inner, and after greet inner function call.

Those are the background knowledge required to understand Closure. Let’s dive in!

Closure:

Closure is a function with an extended scope that contains free variables. A teaser, we already have come across closure. The examples in nonlocal scope is a closure itself.

Let’s see an example with an illustration:

On running the file, we get:

19*2=38
19*3=57

Here the variable num is shared between 2 scopes — multiplication_table and multiply functions. The num is a free variable for function multiply. However, num references the same value. Python does this my creating a cell as an intermediate object. When requesting value of num , Python first looks at the intermediate cell and then go to the object the cell points to get value, “double-hop”.

This diagram illustrates the process:

schematic diagram of how free variable s are referred in closure

This is how it works in Python console. We can look at closure’s information by using __closure__ attribute on a function that is enclosed. There is also __code__.co_free_vars attribute to find free variables in a closure.

Hope this gives you enough understanding to like back closure and face them friendly. 😌

Want a small challenge? Write a fibonacci series using closure. Good luck!

This is my solution.

That’s all for this topic.

Thank you for reading and congratulations on the completion! ☀️

My next article most probably will be on decorators. See you then!

Mmm… I was sitting in one of that single row of sittings that faced perpendicular to white board. 😁

Inspiration:

You can support me on Patreon! Thank you.

--

--