Python provides powerful features like decorators and context managers to simplify and enhance your code. They help in creating reusable, concise, and efficient functionality.
A decorator is a function that modifies the behavior of another function or method. They are used for logging, enforcing access control, instrumentation, and more.
def decorator(func):
def wrapper():
print("Before the function call")
func()
print("After the function call")
return wrapper
@decorator
def say_hello():
print("Hello!")
say_hello()
@decorator
syntax applies the decorator
function to say_hello
.wrapper
function modifies the behavior of say_hello
.def log(func):
def wrapper(*args, **kwargs):
print(f"Function {func.__name__} called with arguments {args} and {kwargs}")
return func(*args, **kwargs)
return wrapper
@log
def add(a, b):
return a + b
print(add(3, 5))
def requires_auth(func):
def wrapper(user):
if not user.get("is_authenticated", False):
raise PermissionError("User is not authenticated")
return func(user)
return wrapper
@requires_auth
def view_dashboard(user):
print("Access granted to the dashboard.")
view_dashboard({"username": "John", "is_authenticated": True})
Overusing decorators can make code harder to debug. Use them judiciously.
Context managers handle setup and cleanup tasks, ensuring proper resource management. They are commonly used with the with
statement.
with open('example.txt', 'r') as file:
content = file.read()
print(content)
# File automatically closed after this block
class MyContext:
def __enter__(self):
print("Entering context")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting context")
with MyContext() as ctx:
print("Inside context")
contextlib
from contextlib import contextmanager
@contextmanager
def my_context():
print("Entering context")
yield
print("Exiting context")
with my_context():
print("Inside context")
The contextlib
module simplifies the creation of context managers for straightforward use cases.
You can combine these two features for advanced use cases, like logging the duration of context execution.
from contextlib import contextmanager
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} executed in {end_time - start_time:.2f} seconds")
return result
return wrapper
@timer
@contextmanager
def managed_resource():
print("Acquiring resource")
yield
print("Releasing resource")
with managed_resource():
print("Using resource")
Understand the lifecycle of the with
statement when using decorators with context managers for complex tasks.
Write a Decorator:
Example:
def timer(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} executed in {end - start:.2f} seconds")
return result
return wrapper
Create a Custom Context Manager:
Example:
class DatabaseConnection:
def __enter__(self):
print("Connecting to database")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Closing database connection")
with DatabaseConnection():
print("Performing database operations")
Combine Both Features:
Example:
def log_context(func):
def wrapper(*args, **kwargs):
print(f"Entering {func.__name__} context")
result = func(*args, **kwargs)
print(f"Exiting {func.__name__} context")
return result
return wrapper
@log_context
@contextmanager
def sample_context():
print("Inside context")
yield
print("Exiting inner context")
with sample_context():
print("Doing work")
Decorators and context managers are invaluable tools for writing Pythonic code. Practice these concepts to enhance your ability to write clean, efficient, and maintainable programs.