Overview
Decorators in Python are a very powerful and useful tool that allow you to modify the behavior of a function or class. They are essentially functions that wrap another function or method, allowing you to add functionality before and after the target function runs, without modifying the function itself. This is important for enhancing the functionality of existing code, implementing cross-cutting concerns like logging, access control, and memoization in a clean and maintainable way.
Key Concepts
- Function Decorators: Modify or enhance functions without changing their definition.
- Class Decorators: Similar to function decorators but used to modify or enhance classes.
- Built-in Decorators: Python includes several decorators like
@staticmethod
,@classmethod
, and@property
.
Common Interview Questions
Basic Level
- What is a decorator in Python?
- How do you write a simple function decorator?
Intermediate Level
- How can decorators be used for logging function execution?
Advanced Level
- Can you explain how to use a decorator to implement a singleton class?
Detailed Answers
1. What is a decorator in Python?
Answer: A decorator in Python is a function that takes another function as an argument and extends the behavior of this latter function without explicitly modifying it. Decorators provide a simple syntax for calling higher-order functions. By definition, a decorator is a callable that returns a callable.
Key Points:
- Decorators can modify or enhance the behavior of the function or method.
- They provide a flexible way to add functionality to code.
- They follow the DRY (Don't Repeat Yourself) principle.
Example:
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
2. How do you write a simple function decorator?
Answer: Writing a simple function decorator involves defining a wrapper function inside another function. The outer function accepts a function as an argument, and the inner function, often called the wrapper, extends the behavior of the original function. The outer function then returns the wrapper function.
Key Points:
- The decorator takes a function as an argument.
- The inner/wrapper function adds functionality before and/or after the original function call.
- The outer function returns the wrapper function.
Example:
def simple_decorator(func):
def wrapper():
print("Before the function call")
func()
print("After the function call")
return wrapper
@simple_decorator
def hello_world():
print("Hello, world!")
hello_world()
3. How can decorators be used for logging function execution?
Answer: Decorators can be used to log the execution of functions by wrapping the function call with logging statements. This allows for monitoring when a function starts and finishes execution, and can also be used to log arguments passed to the function and values returned by it.
Key Points:
- Useful for debugging and monitoring applications.
- Can log function arguments and return values.
- Enhances code readability and maintainability.
Example:
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"Executing {func.__name__} with arguments {args} and {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_decorator
def add(a, b):
return a + b
add(5, 3)
4. Can you explain how to use a decorator to implement a singleton class?
Answer: A singleton is a design pattern that restricts the instantiation of a class to one "single" instance. A decorator can be used to implement this pattern by ensuring that only one instance of the class is created. The decorator checks if an instance already exists; if not, it creates one, otherwise, it returns the existing instance.
Key Points:
- Ensures a class has only one instance.
- Provides a global point of access to that instance.
- Implemented using a decorator that controls the instantiation process.
Example:
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
print("Loading database")
db1 = Database()
db2 = Database()
print(db1 == db2) # True, both variables point to the same instance
This guide covers the basics of decorators in Python, including their definition, usage, and examples of common use cases. Decorators are a powerful feature in Python, allowing for clean and maintainable modifications to the behavior of functions and classes.