Functional Decorators and Higher-Order Functions with functools
This challenge focuses on utilizing the functools module in Python to create powerful and reusable decorators and higher-order functions. Understanding functools is crucial for writing cleaner, more modular, and more Pythonic code, especially when dealing with function composition and memoization. You'll be implementing decorators and functions that leverage functools tools to enhance functionality.
Problem Description
You are tasked with implementing several functions that demonstrate the use of functools in Python. Specifically, you will:
retrydecorator: Create a decorator calledretrythat retries a function a specified number of times if it raises a given exception. The decorator should accept the maximum number of retries and the exception type as arguments.lru_cachedecorator: Implement a function that usesfunctools.lru_cacheto memoize the results of a function. The function should accept the maximum size of the cache as an argument.composefunction: Implement a function calledcomposethat takes a variable number of functions as arguments and returns a single function that is the composition of the input functions. The composition should be performed from right to left (i.e.,compose(f, g, h)(x)should be equivalent toh(g(f(x)))).
Key Requirements:
- The
retrydecorator must correctly retry the decorated function until it succeeds or the maximum number of retries is reached. - The
lru_cachefunction must correctly memoize the results of the decorated function, preventing redundant calculations. - The
composefunction must correctly compose the input functions, applying them in the correct order. - All functions should handle edge cases gracefully (e.g., invalid input, empty function lists).
Expected Behavior:
- The
retrydecorator should raise the specified exception if the function fails after all retries. - The
lru_cachefunction should significantly improve performance for functions with overlapping inputs. - The
composefunction should return a new function that applies the composed functions correctly.
Examples
Example 1: retry decorator
Input:
def my_function():
import random
if random.random() < 0.5:
raise ValueError("Something went wrong")
return "Success!"
@retry(retries=3, exception=ValueError)
def call_my_function():
return my_function()
Output: "Success!" (after potentially multiple retries)
Explanation: The `call_my_function` will retry `my_function` up to 3 times if a ValueError is raised. Eventually, `my_function` will return "Success!" without raising an exception.
Example 2: lru_cache function
Input:
import functools
@lru_cache(maxsize=3)
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(5))
print(fibonacci(4))
print(fibonacci(5))
Output:
5
3
5
Explanation: The `fibonacci` function is memoized with a maximum cache size of 3. The second call to `fibonacci(5)` retrieves the result from the cache instead of recomputing it.
Example 3: compose function
Input:
def add_one(x):
return x + 1
def square(x):
return x * x
def double(x):
return x * 2
composed_function = compose(add_one, square, double)
result = composed_function(2)
Output: 12
Explanation: `composed_function(2)` is equivalent to `double(square(add_one(2)))`. `add_one(2)` is 3, `square(3)` is 9, and `double(9)` is 18.
Constraints
- Retries: The
retrydecorator must accept a non-negative integer for the number of retries. - Cache Size: The
lru_cachefunction must accept a non-negative integer for the cache size. - Function Arguments: The
composefunction can accept any number of functions as arguments. - Performance: The
lru_cachefunction should provide a noticeable performance improvement for functions with overlapping inputs. - Exception Handling: The
retrydecorator should raise the specified exception if the function fails after all retries.
Notes
- The
functoolsmodule provides several useful tools for functional programming. Explore the documentation forfunctools.retry,functools.lru_cache, andfunctools.partialto gain a deeper understanding. - Consider using exception handling within the
retrydecorator to gracefully handle errors. - The
lru_cachedecorator is a powerful tool for optimizing recursive functions and other functions with overlapping inputs. - The
composefunction is a fundamental concept in functional programming, allowing you to combine multiple functions into a single, more complex function. - Think about how to handle edge cases, such as empty lists of functions in the
composefunction. What should the behavior be in these scenarios?