Python Decorators and Closures
What is a Closure in Python?
A closure is a function object that remembers values in the enclosing scope even if the outer function has finished executing. This is a fundamental concept behind decorators.
Example: Python Closure
def outer(msg):
def inner():
print(f"Message: {msg}")
return inner
my_func = outer("Hello from Closure!")
my_func()
Output:
Message: Hello from Closure!
Why is this a closure?
- inner() is nested inside outer()
- inner() uses a variable (msg) from the outer function
- outer() returns the inner() function
Python Decorators Explained
A decorator is a higher-order function that takes a function as input and returns a new function with added functionality—without modifying the original function's code.
How Do Decorators Work?
Decorators typically:
- Accept a function as an argument
- Define an inner function to wrap additional logic
- Return that inner function
Basic Python Decorator Example
def my_decorator(func):
def wrapper():
print("Before the function is called.")
func()
print("After the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
Output:
Before the function is called. Hello! After the function is called.
Example with Arguments
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Function is being called with arguments")
return func(*args, **kwargs)
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Output:
Function is being called with arguments Hello, Alice!
Real-World Use Case: Authorization Decorator
def require_admin(func):
def wrapper(user):
if user == "admin":
return func(user)
else:
print("Access Denied.")
return wrapper
@require_admin
def access_dashboard(user):
print(f"Dashboard accessed by {user}")
access_dashboard("admin") # Access granted
access_dashboard("guest") # Access denied
Output:
Dashboard accessed by admin Access Denied.
Chaining Multiple Decorators
def uppercase(func):
def wrapper():
return func().upper()
return wrapper
def greet_decorator(func):
def wrapper():
return f"Greeting: {func()}"
return wrapper
@uppercase
@greet_decorator
def say_hi():
return "hello"
print(say_hi())
Output:
GREETING: HELLO
Decorators are applied bottom-up.
Closure vs Decorator
Feature | Closure | Decorator |
---|---|---|
Purpose | Preserve state of enclosing scope | Extend behavior of function |
Returns | Inner function | Inner function |
Usage | Reuse logic and encapsulate state | Add features like logging, timing |
Example | Returning functions with preserved values | @decorator syntax above functions |
Built-in Decorators in Python
Decorator | Purpose |
---|---|
@staticmethod | Defines a static method in a class |
@classmethod | Defines a class method |
@property | Turns method into a read-only property |
@functools.wraps | Preserves original function metadata |
Pro Tip: Use functools.wraps to Preserve Metadata
from functools import wraps
def log_function(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling: {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_function
def test():
"""This is a test function."""
print("Running test.")
print(test.__name__) # test
print(test.__doc__) # This is a test function.