Python > Advanced Python Concepts > Decorators > Class Decorators
Class Decorator for Logging Method Calls
This code snippet demonstrates the use of a class decorator to log the entry and exit of methods in a class. This can be particularly useful for debugging and auditing purposes. Class decorators provide a powerful way to modify or enhance the behavior of entire classes in a clean and reusable manner.
Code Implementation
LogMethodCalls
class acts as the decorator. Its __init__
method optionally accepts a logger instance, defaulting to the root logger.__call__
method is the heart of the class decorator. It takes the class itself as an argument.__call__
, it iterates through the class's methods using cls.__dict__.items()
. It filters out special methods (those starting with '__') and applies the log_method
decorator to each callable method.log_method
method takes a method and its name, then returns a wrapped version of the method using functools.wraps
. The wrapped method logs the entry and exit using the logger
instance.MyClass
using the @LogMethodCalls()
syntax.MyClass
is created and the decorated methods are called.
import functools
import logging
# Configure logging (optional)
logging.basicConfig(level=logging.INFO)
class LogMethodCalls:
def __init__(self, logger=None):
self.logger = logger or logging.getLogger(__name__)
def __call__(self, cls):
original_init = cls.__init__
def new_init(self, *args, **kwargs):
self.logger.info(f'Initializing class {cls.__name__}')
original_init(self, *args, **kwargs)
cls.__init__ = new_init
for name, method in cls.__dict__.items():
if callable(method) and not name.startswith('__'):
setattr(cls, name, self.log_method(method, name))
return cls
def log_method(self, method, method_name):
@functools.wraps(method)
def wrapper(*args, **kwargs):
self.logger.info(f'Entering {method_name}')
result = method(*args, **kwargs)
self.logger.info(f'Exiting {method_name}')
return result
return wrapper
@LogMethodCalls()
class MyClass:
def __init__(self, value):
self.value = value
def process_data(self, multiplier):
return self.value * multiplier
def another_method(self, message):
return f'Message: {message}'
# Example Usage
instance = MyClass(10)
print(instance.process_data(2))
print(instance.another_method('Hello'))
Concepts Behind the Snippet
@
syntax, modify the behavior of an entire class. They achieve this by implementing the __call__
method, which receives the class itself as input.__call__
Method: This special method allows an instance of a class to be called as if it were a function. In the context of a decorator, this is how the class decorator modifies the decorated class.functools.wraps
: This decorator preserves the original function's metadata (name, docstring, etc.) when wrapping it, making debugging and introspection easier.
Real-Life Use Case
This type of decorator can be used extensively in application development for tasks such as:
Best Practices
functools.wraps
to maintain the original function's metadata.LogMethodCalls
constructor).
Interview Tip
Be prepared to explain how class decorators work, especially the role of the Common question: What are the advantages of using a class decorator over a function decorator for logging? Answer: Class decorators can maintain state (e.g., a logger instance) and can be more easily configurable.__call__
method. Also, be able to compare and contrast class decorators with function decorators.
When to Use Them
Memory Footprint
The memory footprint of a class decorator is relatively small. It primarily involves storing the wrapped methods. However, excessive logging can consume significant memory, so consider the logging level carefully.
Alternatives
Pros
Cons
FAQ
-
How does a class decorator differ from a function decorator?
A class decorator decorates a class, while a function decorator decorates a function or method. Class decorators typically use the__call__
method to operate on the class itself, allowing modification of multiple methods or the class structure. Function decorators operate on a single function at a time. -
Can I use multiple class decorators on a single class?
Yes, you can apply multiple class decorators. The decorators will be applied in the order they are listed, from top to bottom. -
How can I pass arguments to a class decorator?
The arguments are passed to the class decorator's constructor (__init__
method).