Python Inner Functions
In Python, a function inside another function is called an inner function or nested function. Inner functions help in organizing code, improving readability and maintaining encapsulation. They can access variables from the outer function, making them useful for implementing closures and function decorators.
Example:
def fun1(msg): # outer function
def fun2(): # inner function
print(msg)
fun2()
fun1("Hello")
Output
Hello
Explanation: Here, fun2() is definedinside fun1() and it accesses the variable msg from the enclosing scope.
Why Use Inner functions?
Inner functions provide several advantages:
- Encapsulation: They help hide the inner logic from external access.
- Code Organization: They make the code cleaner by grouping related functionality.
- Access to Enclosing Scope: Inner functions can access variables of the outer function.
- Closures: They allow functions to retain the state of their enclosing function even after execution.
Scope of variables in inner functions
Inner functions can access variables from their enclosing (outer) function, but modifying them requires special handling. This follows Python’s LEGB rule (Local, Enclosing, Global, Built-in) for variable scope.
Example 1 : Local Variable Access
def fun1(): # outer function
msg = "Geeks for geeks"
def fun2(): # inner function
print(msg) # accessing outer function's variable
fun2()
fun1()
Output
Geeks for geeks
Explanation: fun1() defines a local variable msg and an inner function fun2(), which prints msg. Due to lexical scoping, fun2() accesses msg from fun1(). Calling fun1() invokes fun2(), printing the message.
Example 2: Modifying variables using nonlocal
def fun1(): # outer function
a = 45
def fun2(): # inner function
nonlocal a # allows modification of `a` from fun1
a=54
print(a)
fun2()
print(a)
fun1()
Output
54 54
Explanation: nonlocal keyword allows fun2() to modify the variable a from fun1(). Without nonlocal, a inside fun2() would be treated as a new local variable instead of modifying the one in fun1().
Example 3 : closure in inner function
def fun1(a): # outer function
def fun2(): # inner function
print(a)
return fun2 # returning function without parentheses
closure_func = fun1("Hello, Closure!")
closure_func() # inner function remembers 'a'
Output
Hello, Closure!
Explanation: Even after fun1() completes execution, the returned fun2() function retains access to a, demonstrating a closure.
Real - World Applications of inner functions
Inner functions are useful in real-world scenarios for better code organization, encapsulation and reusability. Below are some practical applications:
Example1 : Encapsulation of helper functions
def process_data(data):
# removes extra spaces from a list
def clean_data():
return [item.strip() for item in data] # Strip spaces
return clean_data() # return cleaned list
print(process_data([" Python ", " Inner Function "]))
Output
['Python', 'Inner Function']
Explanation: process_data(data) removes leading and trailing whitespace from each string in the input list. It defines a nested function, clean_data(), which trims spaces using .strip() and returns the cleaned list.
Example 2 : Function wrapper and logging
import logging
logging.basicConfig(level=logging.INFO) # configure logging
def logger(func):
# logs function execution details
def wrapper(*args, **kwargs):
logging.info(f"Executing {func.__name__} with {args}, {kwargs}") # log function call
return func(*args, **kwargs) # call original function
return wrapper
@logger
def add(a, b):
return a + b # return sum
print(add(3, 4))
Output:
INFO:root:Executing add with arguments (3, 4), {}
7
Explanation: logger function, *args captures positional arguments, and **kwargs captures keyword arguments, allowing the wrapper to handle any function signature.
Best Practices for using inner functions
Inner functions are powerful but should be used wisely to maintain code readability, efficiency and maintainability. Below are some best practices:
- Use inner functions only when necessary: Avoid excessive nesting, as it can reduce readability.
- Use closures wisely: Ensure that captured variables are managed properly to prevent unintended side effects.
- Prefer nonlocal over global variables: If modifying outer function variables, use nonlocal instead of global.
- Use inner functions in decorators: This is a common and effective use case.