Closures in Python
About Data Hiding and Small Class Alternatives
In Python, a closure is a nested function, which accesses variables from its enclosing scope, and is returned by another function. This can be useful to avoid small classes containing only a single function, promotes data hiding and offers alternatives to global variables.
In this article we’ll first introduce and describe closures, and then show their application on different examples.

Nested Functions
Before coming to closures, let’s talk about nested functions: in Python, functions can be defined nested, i.e. within each other:
def foo(x):
def bar():
print(f"bar: {x}")
print(f"foo: {x}")
bar()
foo(0)In this examples, foo is the outer function, and inside it, we have defined another function named bar — a nested, or inner function. bar can be called from inside foo, but not from outside, as it is not valid in this scope. Further, we can see that bar has access to foo’s variables, such as x. This is known as a free variable — a variable which is used in a code block, but not defined there.
Closures
With this, we can define closures. Since functions are first-class objects in Python, we can also return them without calling them. Initiating this small change yields a closure from above example:
def foo(x):
def bar():
print(f"bar: {x}")
print(f"foo: {x}")
return bar
my_closure = foo(0)
my_closure()Now, foo returns the function bar, and via my_closure = foo(0) we define a closure — and obtain an object, in which the variable x (in this case 0) is “bound” to the returned object — even though x is not available anymore later. Let us re-visit the definition of a closure: a closer is an inner function, which access variables from its enclosing scope (free variables), and is returned by another function. This is given here.
Of course, this example does not make too much sense yet, so let’s introduce a slightly better one (and refer the reader to the examples at the end of this article):
def greeter(name):
def greet():
print(f"Welcome {name}!")
return greet
peter_greeter = greeter("Peter")
hans_greeter = greeter("Hans")
peter_greeter()
hans_greeter()Above, we define a greeter function, which can be used to generate personal greeting calls.
Modifying Variables Inside Closures
We now extend our above example to also count how often a specific person is greeted, for this introducing a counter variable. However, a naive version will not run — we have to use the nonlocal keyword to allow assigning values to variables from outer scopes:
def greeter(name):
count = 0
def greet():
nonlocal count
count += 1
print(f"Welcome {name}! I have greeted them {count} many times so far.")
return greet
peter_greeter = greeter("Peter")
hans_greeter = greeter("Hans")
peter_greeter()
hans_greeter()
peter_greeter()
peter_greeter()The reason for this is that count is an int, and thus an immutable type. For mutable types, such as Lists or Dictionaries, nonlocal is not required:
def greeter(name):
summary_dict = {"name": name, "count": 0}
def greet():
summary_dict["count"] += 1
print(f"Welcome {summary_dict['name']}! I have greeted them {summary_dict['count']} many times so far.")
return greet
peter_greeter = greeter("Peter")
hans_greeter = greeter("Hans")
peter_greeter()
hans_greeter()
peter_greeter()
peter_greeter()Why Use Closures
Overall, they support the idea of data hiding, and reduce the need for global variables.
Even though related, in my opinion their main use-case is the replacement of classes with just a single function besides __init__(). Consider the simple greeter counter from above. Using a class, this would look as follows:
class GreeterClass:
def __init__(self, name):
self.greet_name = name
self.count = 0
def __call__(self):
self.count += 1
print(f"Welcome {self.greet_name}! I have greeted them {self.count} many times so far.")
peter_greeter = GreeterClass("Peter")
hans_greeter = GreeterClass("Hans")
peter_greeter()
hans_greeter()
peter_greeter()
peter_greeter()It can be argued, that a closure is more concise and to the point.
Examples
Let’s finish with some more realistic examples. In the first, we want to count the number of times a function is called by wrapping it inside a closure. Note this is closely related to Python decorators, and, depending on the use-case, these probably are preferred here.
def call_counter(func):
call_count = 0
def call(*args, **kwargs):
nonlocal call_count
call_count += 1
print(f"Calling {func.__name__}. This is call number: {call_count}.")
func(*args, **kwargs)
return call
def printer(msg):
print(msg)
call_printer = call_counter(printer)
call_printer("call 1")
call_printer("call 2")In the next example we define a short logger:
def create_logger():
log_content = "Start of the log.\n"
def log(msg):
nonlocal log_content
log_content += msg + "\n"
return log_content
return log
logger = create_logger()
log_content = logger("Log A")
log_content = logger("Log B")
print(log_content)Please let me know if you have any questions or comments, and feel free to subscribe if you liked this post!
