medium +25 pts

Benchmark decorator

Build a decorator that tracks call counts and timing statistics.

Write a decorator `benchmark` that wraps any function. The wrapped function must behave exactly like the original, but after each call it updates a `.stats` dictionary attached to the wrapper. The `.stats` dictionary must contain the following keys: - `'calls'`: total number of times the wrapped function has been called (int) - `'total_time'`: sum of all call durations in seconds (float, not rounded) - `'average_time'`: total_time / calls, rounded to 6 decimal places (float) - `'best_time'`: minimum call duration in seconds, rounded to 6 decimal places (float) On the first call, `best_time` equals the first call's duration (rounded). The decorator must preserve the original function's metadata using `functools.wraps`. The wrapper must accept any positional and keyword arguments and return the original function's return value. Use `time.perf_counter()` to measure elapsed time. The decorated function will not raise exceptions. Define the function exactly as: ```python def benchmark(func): pass ``` Additionally, define three helper functions that are tested separately: - `simple_sum(a, b)`: returns `a + b` - `empty_args()`: returns the string `"hello"` - `stats_calls(wrapped)`: returns `wrapped.stats['calls']` (if `wrapped` is a wrapper, otherwise returns 0) - `stats_average(wrapped)`: returns `wrapped.stats['average_time']` (if `wrapped` is a wrapper, otherwise returns 0.0) - `stats_best(wrapped)`: returns `wrapped.stats['best_time']` (if `wrapped` is a wrapper, otherwise returns 0.0) These helpers are used by the test harness to inspect the decorator's behavior. Implement them in your solution code as well.

Constraints

The wrapped function may accept any arguments and return any value. Time measurements are in seconds and are non-negative floats. Round only the average and best times to 6 decimal places. The total time is not rounded.

Example

```python
import time

@benchmark
def add(a, b):
    time.sleep(0.01)
    return a + b

add(1, 2)          # 3
add(3, 4)          # 7
print(add.stats)
# Example output (values will vary):
# {'calls': 2, 'total_time': 0.020012, 'average_time': 0.010006, 'best_time': 0.010000}

# Helper functions usage:
simple_sum(2, 3)          # 5
stats_calls(add)          # 2
```
25 points ~25 min

Recent Submissions

No submissions yet — hit Run Tests to try!

Hints

Use `functools.wraps(func)` to preserve metadata.
Initialize the stats dictionary outside the wrapper function and attach it to the wrapper as an attribute.
Use `time.perf_counter()` before and after the call to compute elapsed time.
After each call, update the stats dictionary; round average and best times with `round(x, 6)`.
Python 3
All tests passed!
Test Results
Press Ctrl+Enter or click Run Tests to execute your code.