easy +8 pts

Class-based decorator with call validation

Implement a class-based decorator that validates argument types and logs successful calls.

Implement a class-based decorator named `validate_and_log`. The decorator class must: 1. Accept an `expected_type` when instantiated, e.g., `@validate_and_log(int)`. 2. The decorated function receives exactly one positional argument `x`. 3. Before calling the wrapped function, verify that `x` is an instance of `expected_type` AND that `x` is numeric (i.e., `isinstance(x, (int, float))`). If either check fails, raise `TypeError(f"Argument must be of type {expected_type.__name__}")`. 4. On success, call the original function with `x`, then append the tuple `(x, result)` to the class-level list `call_log`. 5. Return the result. 6. Use `functools.wraps(func)` to preserve metadata (`__name__`, `__doc__`) of the original function. Your implementation must be a class named `validate_and_log`. The `__init__` stores `expected_type` as an instance attribute. The `__call__` receives the function and returns a wrapper. The wrapper takes only one positional argument `x`. No keyword arguments are ever passed. After defining the class, use it to decorate: - `double(x)` returns `x * 2`, decorated with `@validate_and_log(int)`. - `float_function(x)` returns `x`, decorated with `@validate_and_log(float)`. Also define helper functions `double_type_error(x)` (calls `double(x)` and returns the TypeError message string) and `call_log_after()` (returns `validate_and_log.call_log` as a list of lists, e.g., `[[4, 8], [0, 0]]`).

Constraints

The decorated functions are always called with exactly one positional argument. `expected_type` is always `int` or `float`. The `call_log` list is class-level, shared across all instances, and initialized as an empty list at class definition time. Complexity is trivial; no input size limits.

Example

>>> @validate_and_log(int)
... def double(x):
...     return x * 2
>>> double(5)
10
>>> validate_and_log.call_log
[(5, 10)]
>>> double.__name__
'double'
>>> try:
...     double("5")
... except TypeError as e:
...     print(e)
Argument must be of type int

>>> @validate_and_log(float)
... def float_function(x):
...     return x
>>> float_function(2.5)
2.5
>>> [list(item) for item in validate_and_log.call_log]
[[5, 10], [2.5, 2.5]]
8 points ~15 min

Recent Submissions

No submissions yet — hit Run Tests to try!

Hints

Use `isinstance(x, self.expected_type)` and `isinstance(x, (int, float))` together with `and`.
After computing `result = func(x)`, append `(x, result)` to `validate_and_log.call_log`.
Decorate the inner wrapper with `@functools.wraps(func)` to preserve metadata.
Python 3
All tests passed!
Test Results
Press Ctrl+Enter or click Run Tests to execute your code.