Problem No 19
Implement a decorator that logs function calls
Hard≈ 24 minute session
Lesson guide
What this Python exercise practices
Implement a decorator that logs function calls is a advanced practice lesson that focuses on functions, parameters, return values. It is designed to be solved in about 24 minutes with examples, starter code, and test feedback.
Prerequisites
- Python variables
- Function parameters
- Return values
Difficulty and time
- Level
- Advanced
- Estimated time
- 24 minutes
Practice path
Summary
Create a decorator that records every call made to a function and preserves the function's behavior and metadata.
Problem statement
Write a decorator named log_calls. When applied to a function, the decorator should return a wrapped function that: - Returns the same value as the original function when called. - Keeps an attribute .calls on the wrapped function that is a list. Each entry must be a tuple (args, kwargs) where args is the tuple of positional arguments and kwargs is a dict of keyword arguments passed for that call. - Preserves the original function's __name__ and __doc__ (use functools.wraps or equivalent). - Does not print anything. You will be provided with small factory helper functions (make_adder, make_greeter, make_combo) that create decorated functions for you to test against. Implement only the decorator; do not modify the helpers' behavior.
Task
Implement a decorator log_calls that wraps a function, records each call's args and kwargs in an attribute .calls, preserves the original return value, and keeps the function's name and docstring.
Examples
Basic usage
Input
(lambda f: (f(2, 3), f.calls))(make_adder())
Output
(5, [((2, 3), {})])
Explanation
make_adder() returns an 'add' function decorated with @log_calls. Calling it with (2,3) returns 5 and records the call as ((2, 3), {}).
Input format
Your code must define a decorator function named log_calls and the provided helper factories (make_adder, make_greeter, make_combo) will be used in tests.
Output format
Expressions evaluated in tests will return values (e.g., tuples, lists, strings). The .calls attribute must be a list of (args, kwargs) tuples.
Constraints
- Do not print anything from the decorator or wrapped function. - The .calls attribute must exist on the decorated function object and be a list. - Each recorded kwargs must be an ordinary dict (not a view). - Preserve __name__ and __doc__ of the original function. - The decorator must work for functions with positional and keyword arguments, and multiple calls should accumulate in .calls.
Samples
Sample input 0
(lambda f: (f(), f.calls))(make_greeter())
Sample output 0
('Hello, World!', [((), {})])
Explanation 0
make_greeter() returns a function with a default parameter. Calling it without args returns 'Hello, World!' and logs the empty args tuple and empty kwargs dict.
AI assistant
Ask me anything!
Need help? I can explain the core idea behind this problem, review your current code, and give targeted hints. Use “Teach Theory” for the concept, “Get AI hint” for a quick scaffold nudge, or ask a specific question below.
Chat history is temporary and will not be saved.
Free preview includes 1 Teach Theory response and 1 AI hint per unlocked preview lesson.