Python closure functions may seem intimidating due to their rather unintuitive syntax, but mastering them is worthwhile if you want to write truly expressive and maintainable Python code.

In this article, we’ll go through code examples that reveal that closures are actually quite simple. Let’s dive in!

Python’s Nested Functions and Scope

To understand closure functions, we first have to explain Python’s nested functions and the concept of scope. We’ll explain these concepts through this example:

A nested function is a function that’s defined within another function. In our example above, “shout()” is a nested function since it’s defined within “greet().” We say that “shout()” is locally scoped to “greet(),” which means that it does not exist outside of “greet(),” as shown by the following code snippet:

In Python, nested functions may access the state of the environment in which they were defined — we call this the enclosing scope of a function. This property allows “shout()” to access the value of “name” even though “name” does not belong to the scope of “shout(),” but rather to that of its parent function, “greet().”

In the previous example, “name” is a non-local variable to “shout()” since we defined “name” outside of the latter’s scope. Next, we give another example of scope.

Although “x” exists in the global scope and has the value 10, “x” has a different value within the local scope of “square().” Functions generally prioritize accessing the variables of the innermost scope.

Python Closures

Let’s take a look at a slightly modified definition of “greet()”:

We redefined “greet()” to create a “shout()” function and return the latter instead of just calling it. Then, we called “greet(name=“again”)” and assigned the output to “greet_again.” 

“greet()” finished its execution when we called it, after which we assigned the result of “greet()” to “greet_again.” However, this was not the case for “shout()”; it would only start executing when we were to call “greet_again(),” at which point “shout()” would execute the statement that references the “name” variable. And despite “name” belonging to the scope of “greet()” (a function that has already finished execution), “shout()” can access “name’s” value and successfully execute the print statement.

Next we’ll go over an example of closure in Python. Closure is a property of a function to remember the variables of the enclosing function even following the enclosing function’s execution. In fact, closure applies even when the enclosing function is deleted:

We can also check which object “greet_again” is referencing:

“greet_again” references the “shout() function,” which will have access to the state of the parent function even after it’s deleted.

The Keyword “nonlocal”

In Python, closures grant nested functions read-only access to variables of the enclosing scope. In other words, the inner function may access but not modify non-local variables. The following example is illustrative:

When we assign to a variable within a given scope, that variable becomes local in that scope. In our example above, when we assign “name*2” to “name,” Python interpreter renders “name” as local to “shout(),” ignoring variables of the same name that exist in the outer scope. This creates a problem since we’re trying to reference “name” before it’s even assigned a value, thus resulting in the UnboundLocalError.

In order for the inner function to modify non-local variables, we need to refer to them using the keyword “nonlocal.”

After we declare “name” as “nonlocal,” “shout()” is finally able to modify “name’s” value, as shown by the last two lines of output in the example above.

Python Nested Functions vs. Python Closures

Not all nested functions are closures. For a nested function to be a closure, the following conditions need to be met: 

  1. The nested function executes after the parent function has completed.
  2. The nested function has access to the non-local variables of the parent function’s scope. 

The example below is a modified version of a “greet()” function that’s not a closure.

Because we passed “name” to “shout(),” “name” ends up belonging to “shout()’s” local scope, causing non-fulfillment of the second condition.

Now that we’ve discussed the underpinnings of Python closures, let’s look at an example of when you might want to use them.

Example of Python Closure: Decorators

Decorators are functions that encapsulate other functions and modify them. Decorators make use of Python’s closure property to change the behavior of existing functions without changing their definition.

Let’s define “uppercase(),” a function that prints the uppercase version of a string we pass to it.

This function works well on strings, but it will not work on objects that do not entail the “upper()” method, such as integers.

Rather than having our function return this arcane looking error every time it receives an argument that’s not a string, we can use decorators and modify the function to print a more user-friendly message.

The definition above should look familiar — it’s another example of a Python nested function! 

Our decorator function, named “decorator(),” contains a nested function that checks whether a condition is fulfilled. In this case, it ensures that the received string is indeed a string and not anything else, before calling the function “func.” Note how “wrapper()” calls “func” even though func belongs to the “decorator()’s” local scope; this is another example of closure in Python.

We can call our decorator function with the “uppercase()” function.

However, we typically prefer to decorate a function by prefixing an @ symbol to the function’s definition, as below:

While the two calls return the same output, we prefer the latter syntax for its improved code readability.

Keep Learning

Congrats! Now that you know about nested functions and closures, you’re well on your way to mastering Python. But there’s much more for you to learn along your Python journey. 

Get up to speed with our Introduction to Programming Nanodegree and make headway in a fast-growing field such as Web and App Development, Machine Learning, Data Science or AI.

Start Learning