Menu

Sign in to track your progress and unlock all features.

Theme style

Log in

Full lesson preview

Implement a decorator that logs function calls

Create a decorator that records every call made to a function and preserves the function's behavior and metadata.

Python practice24 minFunctions & ScopeAdvancedLast updated March 17, 2026

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), {})])

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 1

Input

(lambda f: (f(), f.calls))(make_greeter())

Output

('Hello, World!', [((), {})])

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.