medium +20 pts

Cache result decorator

Implement a decorator that caches function results based on positional arguments.

Write a decorator `cache_result` that caches the return value of a function based on its positional arguments. The decorator should: - Accept positional arguments only (no keyword arguments) for simplicity. - For each unique combination of positional arguments, the function should be executed only once; subsequent calls with the same arguments should return the cached result. - The function's name and docstring should be preserved (hint: use `functools.wraps`). - The cache should be stored as an attribute on the wrapped function, named `cache`, which is a dictionary mapping a tuple of arguments to the result. - If the function raises an exception, the exception should propagate and the result should NOT be cached. Implement the decorator function with signature `def cache_result(func):` that returns the wrapped function. You are required to define the decorator and also provide test functions decorated with it: `add`, `greet`, `mult`, `zero`, and `counter`. - `add(a, b)` returns `a + b`. - `greet(name)` returns `"Hello, " + name + "!"`. - `mult(a, b, c)` returns `a * b * c`. - `zero()` returns `42`. - `counter()` increments a nonlocal counter each time it is actually executed and returns the current count. The counter should start at 0 and increment by 1 each call; when cached, the result is reused without executing again. Also define a function `check_cache_attr()` that returns `True` if the `add` function has an attribute named `cache` that is a dictionary, otherwise `False`. Note: The decorator is expected to handle functions of any number of positional arguments (including zero). For zero arguments, the key should be an empty tuple.

Constraints

The decorated function will only be called with positional arguments (no keyword arguments). The function may receive 0 to several arguments. Arguments are hashable (e.g., int, float, string, tuple). The function may return any value. The number of unique calls is small enough that a plain dictionary is fine.

Example

```python
@cache_result
def add(a, b):
    return a + b

print(add(2, 3))  # 5
print(add(2, 3))  # 5 (cached)
print(add.cache)  # {(2, 3): 5}
```
20 points ~20 min

Recent Submissions

No submissions yet — hit Run Tests to try!

Hints

Use `functools.wraps` to preserve metadata.
Store the cache dictionary inside the wrapper function and attach it to the wrapper as an attribute.
The cache key is a tuple of `args`.
Only cache successful returns; exceptions propagate without caching.
For `counter`, use a nonlocal variable inside the decorated function to track the count.
Python 3
All tests passed!
Test Results
Press Ctrl+Enter or click Run Tests to execute your code.