Can you explain the concepts of memoization and caching in Python? How would you implement them to improve performance?
Memoization involves caching the results of expensive function calls and returning the cached result when the same inputs occur again. Here's a simple example using a decorator:
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)