A nested function is a function defined inside another function. It can serve as a local helper, or it can be returned as a callable that retains access to values from its enclosing function. That second pattern—using a closure—is the basis of function factories, many callbacks, and decorators.
Define a function inside another function
Use an indented def statement to define an inner function:
def outer():
def inner():
return "Hello from inner"
return inner()
The name inner is bound in outer’s local scope. Returning inner() calls it immediately and returns its result. Returning inner without parentheses returns the function object instead:
def outer():
def inner():
return "Hello from inner"
return inner
say_hello = outer()
print(say_hello())
Here, outer() creates and returns inner; the later call to say_hello() runs it. Python’s function-definition rules allow a locally defined function to access free variables from its containing function.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errors#1 Best Overall
- Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
- Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
- Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
- Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
- 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.
How names are resolved in nested functions
When Python evaluates a name used inside a function, it looks through these scopes in order:
- The current function’s local scope
- Enclosing function scopes, from nearest outward
- The module’s global scope
- The built-in scope
This order is commonly called LEGB. For example, the inner function below prints the nearest enclosing message, not the module-level one:
message = "module"
def outer():
message = "outer"
def inner():
print(message)
inner()
outer() # outer
Scope follows where a function is defined, not where it is called. If a nested function cannot find a name locally, it searches its enclosing function scopes before looking at the module. The Python scopes and namespaces tutorial and execution model describe these lookup rules.
Closures retain access to enclosing values
A closure is a function that retains access to names from an enclosing scope after that scope’s function has returned. This makes it possible to configure a function once and call it later:
def make_greeter(name):
def greet():
return f"Hello, {name}!"
return greet
greeter = make_greeter("Maya")
print(greeter()) # Hello, Maya!
The returned function can still resolve name when called. Conceptually, it is useful to think of the callable as function code together with access to the enclosing bindings it needs. It is not necessary to inspect or manipulate that machinery in ordinary code.
Python exposes closure details for debugging. A function’s __code__.co_freevars lists free-variable names, while __closure__ contains cells for those names:
print(greeter.__code__.co_freevars) # ('name',)
print(greeter.__closure__[0].cell_contents) # Maya
For broader inspection, inspect.getclosurevars() reports referenced nonlocal, global, built-in, and unresolved names; see the inspect documentation. Treat these as introspection tools, not as the usual way to manage a closure’s state. The function data model documents closure cells.
Rank #2
- 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
- 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
- Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
- 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
- What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
Use nested functions to create specialized callables
A function factory returns a new function configured with values supplied to the outer function. Each factory call creates a callable with its own enclosed configuration:
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →def make_discount(percent):
def apply_discount(price):
return price * (1 - percent / 100)
return apply_discount
student_discount = make_discount(15)
vip_discount = make_discount(25)
print(student_discount(100)) # 85.0
print(vip_discount(100)) # 75.0
The caller gets a function it can use repeatedly without passing the percentage on every call. This keeps configuration out of globals and allows several differently configured callables to coexist.
Callbacks with private context
A nested function also works well as a callback when the callback needs a value from the surrounding operation:
def make_validator(minimum):
def validate(value):
return value >= minimum
return validate
is_adult = make_validator(18)
adults = list(filter(is_adult, [12, 18, 25]))
Nested functions are not required for callbacks. They are useful when a callback needs local context without a global variable or a separate object.
Private helper logic
Keep a helper nested when it is meaningful only within one operation:
def parse_and_sum(text):
def parse_number(token):
return int(token.strip())
numbers = [parse_number(token) for token in text.split(",")]
return sum(numbers)
This limits the helper name to the operation and keeps related implementation details close together. It is practical name hiding, not a security boundary. Move the helper to module scope or a class if it needs independent reuse, documentation, or testing.
Use nonlocal when an inner function rebinds outer state
An inner function can read a value from an enclosing function without a declaration. If it must rebind that name, use nonlocal:
Rank #3
- Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
- Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
- Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
- Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
- What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.
def make_counter(start=0):
count = start
def next_count():
nonlocal count
count += 1
return count
return next_count
counter = make_counter(10)
print(counter()) # 11
print(counter()) # 12
Without nonlocal, the assignment in count += 1 makes count local to next_count. Python then tries to read that local before it has a value, raising UnboundLocalError. nonlocal makes the name refer to a binding in the nearest enclosing function scope; it raises SyntaxError if no suitable enclosing binding exists. These rules are specified in the nonlocal statement reference.
Mutating an object held by an enclosing name is different from rebinding that name. Mutation does not require nonlocal:
def make_appender():
items = []
def append(item):
items.append(item) # mutates the list; no rebinding
return items
return append
By contrast, assigning a new value to an enclosing name requires nonlocal. Use global instead only when the intended binding is at module scope; global and nonlocal target different scopes. The name-binding rules explain how assignments determine local bindings.
Nested functions power decorators
A decorator commonly defines a wrapper function that closes over the function being decorated:
from functools import wraps
def log_calls(function):
@wraps(function)
def wrapper(*args, **kwargs):
print(f"Calling {function.__name__}")
result = function(*args, **kwargs)
print(f"Returned {result!r}")
return result
return wrapper
@log_calls
def add(a, b):
return a + b
The decorator syntax is approximately equivalent to defining add and then assigning add = log_calls(add). The decorator receives the function object, and its return value replaces the original binding. @wraps(function) preserves useful metadata such as the wrapped function’s name and docstring, and sets __wrapped__ to support introspection and unwrapping. See Python’s decorator definition rules and functools.wraps documentation.
Decorator factories add a configuration layer
When a decorator accepts arguments, add an outer function to capture them. The decorator it returns receives the target function, and the wrapper runs when that function is called:
from functools import wraps
def repeat(times):
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
result = None
for _ in range(times):
result = function(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def say_hi():
print("Hi")
The layers are repeat(3), which returns decorator; decorator(function), which returns wrapper; and wrapper(*args, **kwargs), which runs on each call. For stacked decorators, @outer above @inner is approximately function = outer(inner(function)).
Rank #4
- Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
- Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
- Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
- Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
- Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
Avoid late-binding surprises in loops
Functions created in a loop can all refer to the same enclosing variable binding. The lookup happens when a function is called, so this example uses the final value of factor for every function:
def make_multipliers():
functions = []
for factor in [1, 2, 3]:
def multiply(value):
return factor * value
functions.append(multiply)
return functions
multipliers = make_multipliers()
print([function(10) for function in multipliers]) # [30, 30, 30]
One fix is to bind the current value as a default argument. Default values are set when each function is defined:
def make_multipliers():
functions = []
for factor in [1, 2, 3]:
def multiply(value, factor=factor):
return factor * value
functions.append(multiply)
return functions
Another fix is to call a factory for each value, creating separate enclosing scopes:
Free tools Windows power users keep installed
One-click scans. No signup required.
def make_multiplier(factor):
def multiply(value):
return factor * value
return multiply
multipliers = [make_multiplier(factor) for factor in [1, 2, 3]]
These techniques work differently: a default argument stores the current object in the function’s defaults; a factory creates a distinct enclosing binding for each call. Comprehension loop variables have their own scope in modern Python, but functions created in a comprehension can still have the same late-binding issue:
functions = [lambda: number for number in range(3)]
print([function() for function in functions]) # [2, 2, 2]
functions = [lambda number=number: number for number in range(3)]
The comprehension scope rules do not freeze a value referenced by a function created inside the comprehension.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Choose between a nested def, a lambda, and a class
Use a nested def for named behavior
A nested def is usually clearer when behavior has several statements, needs a docstring or annotations, or deserves a meaningful name for debugging and testing.
Use a lambda only for a small expression
A lambda can also close over enclosing values:
def make_incrementer(amount):
return lambda value: value + amount
It is a compact option for a simple expression. A normal def supports multiple statements and annotations and is generally easier to inspect. The Python tutorial on lambda expressions describes the distinction.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Best Value
- 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
- Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
- Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
- HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
- What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
Use a class when state and behavior grow
A closure is a good fit for a small amount of private state and a callable-focused interface. A class can make state clearer when there are several fields or operations, a public object identity, or a need for subclassing:
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
The closure version of a counter is concise; the class version makes the state an explicit attribute. Neither is universally better. If a closure accumulates many mutable variables or requires substantial independent testing, a class or dedicated state object is often easier to maintain.
Nested functions in classes and recursive helpers
Functions nested in methods
A function defined inside a method can close over that method’s local values:
class Report:
def formatter(self, prefix):
def format_line(value):
return f"{prefix}: {value}"
return format_line
A method does not automatically resolve a name from the class body as though that body were an enclosing function. Access class attributes through self or the class itself:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteclass Example:
label = "class label"
def method(self):
return self.label
The execution model’s name-resolution rules distinguish class scopes from enclosing function scopes. Python 3.12 introduced annotation scopes, which have special rules; they are not ordinary nested function scopes. See the annotation scopes reference.
Recursive helpers
A nested function can keep a recursive helper local to the operation that uses it:
def factorial(n):
def visit(value):
if value <= 1:
return 1
return value * visit(value - 1)
return visit(n)
Nesting here is an organization choice, not a speed or memory optimization. A module-level helper may be easier to test or inspect when the recursive logic becomes substantial.
Quick Recap
When to reconsider a nested function
- Move it to module scope if several parts of the program need to reuse or test it independently.
- Consider a class if the function carries many pieces of mutable state or exposes several related operations.
- Make dependencies explicit when readers would otherwise need to inspect several enclosing scopes to understand the function.
- Do not assume a local function can be serialized or transferred between processes; check the serialization mechanism and its requirements.
- Remember that limiting a name to a local scope is not a security control.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.

