In Post #104, we learned that functions in Python are “first-class citizens,” meaning they can be treated like any other object—assigned to variables, stored in lists, and even passed to other functions. This flexibility allows for some powerful structural patterns, one of which is the ability to define a function inside of another function.
In this post, we’ll explore nested functions. We’ll learn how to create them, understand their unique scope, and see how they provide a new level of organization in our code.
What is a Nested Function?
A nested function is simply a function that is defined inside another function. The outer function is sometimes called the enclosing function, and the function defined inside it is the nested function.
def outer():
print("Outer function is running.")
# This function is defined inside the 'outer' function's scope
def inner():
print("Inner function is running.")
# You must call the inner function from within the outer one for it to run
print("Outer function will now call inner function.")
inner()
# Calling the outer function will, in turn, call the inner one
outer()
The output shows the flow of execution:
Outer function is running.
Outer function will now call inner function.
Inner function is running.
The Scope of a Nested Function
A key rule to understand is that the inner function is local to the outer function. It exists only within the outer function’s scope and cannot be seen or called from anywhere else in your program.
# This will cause a NameError!
# inner()
Attempting to call inner()
from the global scope will fail because inner
is not defined there. This is a form of encapsulation. It allows you to create “helper” functions that are hidden from the rest of your program. If inner()
is only ever needed by outer()
, there’s no reason to clutter the global scope with it.
The Enclosing Scope (The ‘E’ in LEGB)
Remember the LEGB rule for variable lookup from Post #98 (Local, Enclosing, Global, Built-in)? Nested functions are what give us the “E”—the enclosing scope.
This means that the inner function can read variables from the outer (enclosing) function’s scope. This is an incredibly powerful feature.
def greet_person(name):
# 'greeting' is in the enclosing scope for 'say_hello'
greeting = "Hello"
def say_hello():
# 'say_hello' can access both its own scope and the enclosing scope
print(f"{greeting}, {name}!")
# Call the inner function
say_hello()
greet_person("Alice")
When say_hello()
is called, it needs the greeting
and name
variables.
- It first looks in its own Local scope. It doesn’t find them.
- It then looks in the Enclosing scope (the scope of
greet_person
). It finds bothgreeting
andname
there! - It uses these variables to print
Hello, Alice!
.
What’s Next?
Nested functions are a powerful tool for organizing your code and creating helper utilities that are hidden from the global scope. They also introduce the concept of an “enclosing scope,” allowing inner functions to access the state of their containing functions. This is the basis for more advanced patterns like closures and decorators, which you will encounter later in your Python journey.
Defining a full function with def
is great for complex tasks, but sometimes you need a quick, simple function for a single, brief operation. For these situations, Python provides a way to create small, anonymous functions in one line. In Post #106, we will learn about lambda functions.
Author

Experienced Cloud & DevOps Engineer with hands-on experience in AWS, GCP, Terraform, Ansible, ELK, Docker, Git, GitLab, Python, PowerShell, Shell, and theoretical knowledge on Azure, Kubernetes & Jenkins. In my free time, I write blogs on ckdbtech.com