Decorator Delight: Timing and Logging Functions
Decorators are a powerful feature in Python that allow you to modify or enhance the behavior of functions or methods without actually changing their code. This challenge will guide you through implementing decorators to both time the execution of functions and log their calls with arguments and return values. Understanding decorators is crucial for writing clean, reusable, and maintainable code.
Problem Description
You are tasked with creating two decorators: timer and logger.
-
timerDecorator: This decorator should measure and print the execution time of a function. It should take no arguments. The output should be a formatted string indicating the function name and its execution time in seconds, rounded to four decimal places. -
loggerDecorator: This decorator should log the function call, including the function name, arguments, and the return value. The log message should be a formatted string: "Function '{function_name}' called with arguments: {args}, returned: {return_value}". It should also take no arguments.
You need to implement both decorators and apply them to example functions to demonstrate their functionality.
Examples
Example 1:
def add(x, y):
return x + y
@timer
@logger
def my_add(x, y):
return add(x, y)
my_add(5, 3)
Output:
Function 'my_add' called with arguments: (5, 3), returned: 8
Execution time: 0.0000 seconds
Example 2:
def multiply(x, y):
return x * y
@timer
def my_multiply(x, y):
return multiply(x, y)
my_multiply(4, 6)
Output:
Function 'my_multiply' called with arguments: (4, 6), returned: 24
Execution time: 0.0000 seconds
Example 3: (Edge Case - Function with no arguments)
def greet():
return "Hello!"
@timer
@logger
def my_greet():
return greet()
my_greet()
Output:
Function 'my_greet' called with arguments: (), returned: Hello!
Execution time: 0.0000 seconds
Constraints
- The
timerdecorator should use thetimemodule to measure execution time. - The
loggerdecorator should use theinspectmodule to retrieve function arguments. - The execution time should be rounded to four decimal places.
- The decorators should work correctly with functions that have or do not have arguments.
- The decorators should be able to be chained (as shown in Example 1).
Notes
- Remember that decorators are essentially functions that take another function as an argument and return a modified version of that function.
- The
inspectmodule is helpful for introspecting functions and accessing their arguments.inspect.signatureandinspect.getfullargspecare useful functions within theinspectmodule. - Consider using closures to preserve the original function's identity and arguments within the decorator.
- Think about how to handle the return value of the decorated function within the
loggerdecorator. - The order of decorators matters when chaining them. The innermost decorator is executed first.