Introduction
I am sure that you might have heard about decorators in Python. The decorators are one of the popular and the most common use-cases of closure. So, what are Python closures?
To understand closures in Python, you should know about nested function, free variable, and namespaces & scope. So, we will start our discussion with the nested function first followed by free variable and scope, and then move on to the main topic of the article i.e. closures.
In this article, you will understand what are Python closures, examples, when should you use closures, etc. So, let’s get started.
Nested functions
A function that is defined inside another function is called a nested function. The nested function is also called an inner function and the enclosing function is also called the outer function.
In the below example, the greeting is the enclosing (or outer) function and print_message is the nested (or inner function). The variable name
is defined in the outer function greeting() but still, we are able to access it inside nested function print_message() according to the LEGB rule.
>>> def greeting():
... name = 'Python Simplified'
... def print_message():
... print(f'Welcome to {name} !!')
... print_message()
>>> greeting()
Output –
Welcome to Python Simplified !!
As per the LEGB rule, Python looks for the variable in the local scope i.e. scope of print_message function scope first. Since the name is not defined in the inner scope, Python next looks in the enclosing scope i.e. scope of the greeting function scope, and finds the reference there.
If you don’t know scope of the variable and LEGB rule, I invite you to go through our blog on namespaces and scope in Python.
Free Variable
A free variable is a type of variable that is used inside a code block but it’s not defined in that code block. In the above example, the variable name
is defined in the outer function greeting but you are accessing inside inner function print_message. Since the name is not defined in the inner function it is called a free variable.
Now that you know the prerequisites such as nested function, free variable, and scope, let’s jump to the main topic, closures.
What are closures in Python?
In simple terms, you can define closure as the nested/inner function that has access to the free variables from the enclosing function. The more detailed definition of closure is that it should satisfy the below characteristics/conditions in order to call the nested function a closure.
- Closures must contain nested/inner functions.
- The nested function must refer to free variables (i.e. variables that are defined in the outer scope/enclosing scope)
- The outer function/enclosing function must return the inner/nested function
This is how a closure looks like — We will take the same example that we saw earlier with minor changes. As you can see below, in line#5, instead of calling the inner function, we are returning the inner function i.e. return print_message
. Next, you need to call the greeting() function that returns a closure to the variable greet. Finally, when we call greet() it executes the inner function as expected.
Congratulations! You just created your first closure in Python.
>>> def greeting():
... name = 'Python Simplified'
... def print_message():
... print(f'Welcome to {name} !!')
... return print_message
>>> greet = greeting()
>>> greet()
Output –
Welcome to Python Simplified !!
In the above example, the inner function print_message() refers to the free variable name of the outer function greeting() and the outer function returns the inner function print_message. This satisfies all three conditions of closure.
Hope that the below example taught you how closures in Python look like and how to create one. Now, let’s see a few more examples of closures to make our understanding stronger.
Examples
(1) In the below example, we are making use of the nonlocal keyword that references the variable in the enclosing scope i.e. count. Every time closure is called count value is incremented by 1.
In this example, we are only printing a simple message to the console however you can put any business logic insider counter_inner function.
>>> def create_counter():
... count = 0
... def counter_inner():
... nonlocal count
... count += 1
... print(f'Closure called {count} times')
... return counter_inner
>>> counter = create_counter()
>>> c1 = counter()
Closure called 1 times
>>> c2 = counter()
Closure called 2 times
(2). Here is another example where every time a closure is called the num is added to the list data and returns the average value of all the numbers added so far.
>>> def make_averager():
... data = []
... def averager(num):
... data.append(num)
... return sum(data) / (len(data)*1.0)
... return averager
>>> avg = make_averager()
>>> avg(10)
10.0
>>> avg(20)
15.0
>>> avg(30)
20.0
(3). The most common and popular use case of closures is decorators. Decorators add additional functionality to an existing code. We will have a detailed article on decorators in the future but here is a simple example.
One of the real-world use cases of decorators is calculating the time taken by a function to execute. In the below example, calculate_time is the decorator function and myfunc is the function that is being decorated. So, when myfunc(2) is called, myfunc is passed as an argument to the calculate_time function as func. Then myfunc() is called and executed in line#6.
>>> import time
>>> def calculate_time(func):
... def wrapper(*args):
... t = time.time()
... res = func(*args)
... print(f"Function {func.__name__} took " + str(time.time()-t) + " seconds to run")
return res
... return wrapper
>>> @calculate_time
>>> def myfunc(n):
... time.sleep(n)
>>> if __name__ == "__main__":
... myfunc(2)
Output –
Function myfunc took 2.007890462875366 seconds to run
What are the benefits of closures in Python?
Now that you know the definition of closure and how to create one. The question is why should you even consider using it? These are some of the reasons:
(a). Closures avoid unnecessary use of classes
If a class contains only one method (of course, other than the __init__ method), then you should consider using a closure instead of creating a class. Because closures are more readable and avoid unnecessary code thereby savings programmers time.
We are using the example Averager we saw in the previous section. As you can see from the below comparison the use of closure is more readable than creating a class.
Using class: –
>>> class Averager():
... def __init__(self):
... self.data = []
...
... def __call__(self, val):
... self.data.append(val)
... return sum(self.data) / (len(self.data)*1.0)
>>> avg = make_averager()
>>> avg(10)
10.0
>>> avg(20)
15.0
>>> avg(30)
20.0
Using closures: –
>>> def make_averager():
... data = []
... def averager(num):
... data.append(num)
... return sum(data) / (len(data)*1.0)
... return averager
>>> avg = make_averager()
>>> avg(10)
10.0
>>> avg(20)
15.0
>>> avg(30)
20.0
(b). Closures avoid the use of global variables
Sometimes the variables defined in the global scope are not used by all the functions. In such a scenario, closures are helpful so that you can define the variable in the outer/enclosing and use them in the inner scope.
(c). Closures provide data hiding
The inner function of a closure can’t be called directly. You can only access the inner function by calling the outer function. This provides some level of data hiding.
(d). Closures are used in decorators
Closures are commonly used in decorators. A decorator is a function that takes another function as an argument and returns a closure. We have already seen an example in the previous section that how closures are used in decorators.
Summary
- Closures must satisfy three conditions — nested function, free variable, and the outer function returning inner function.
- A function that is defined inside another function is called a nested function.
- A free variable is a type of variable that is used inside a code block but it’s not defined in that code block.
- Some of the advantages of using closures are — they avoid unnecessary use of classes & global variable, provides data hiding.
- The most common use of closures is decorators.