Here are 10 Python snippets using functools.lru_cache to demonstrate caching function results and optimizing performance:
1. Basic Caching with lru_cache
The lru_cache decorator caches the results of a function to optimize performance for expensive, repeated function calls.
Copy
import functools
@functools.lru_cache(maxsize=3)
def expensive_function(x):
print(f"Computing {x}...")
return x * x
print(expensive_function(2)) # First call, computes the result
print(expensive_function(2)) # Second call, returns cached result
2. Caching with maxsize
The maxsize parameter determines how many results the cache can hold. If the cache exceeds the limit, the least recently used (LRU) result is removed.
Copy
import functools
@functools.lru_cache(maxsize=2)
def square(x):
print(f"Computing square of {x}...")
return x * x
print(square(2)) # Computed
print(square(3)) # Computed
print(square(2)) # Cached
print(square(4)) # Computed, evicts the cache for 3
3. Using lru_cache on Recursive Functions
lru_cache is useful for caching results in recursive functions, such as computing Fibonacci numbers.
Copy
4. Checking Cache Statistics
The cache_info method can be used to view statistics on the cache's performance, such as hits, misses, and the current cache size.
Copy
5. Clearing the Cache with cache_clear
You can clear the cache manually with the cache_clear method.
Copy
6. Custom Cache Size
You can adjust the maxsize parameter to store a specific number of results in the cache.
Copy
7. Using lru_cache with Keyword Arguments
lru_cache caches function results based on both positional and keyword arguments.
Copy
8. Caching Functions with No Arguments
Even if a function doesn't take any arguments, you can still apply lru_cache to optimize repeated calls.
Copy
9. Working with Immutable Arguments for Caching
lru_cache requires that function arguments be hashable, as it uses these arguments to create a cache key.
Copy
10. Using lru_cache with Function Wrapping
You can use lru_cache as part of a custom wrapper function to add caching behavior dynamically.
Copy
These snippets demonstrate how functools.lru_cache can be used to improve performance by caching the results of functions, especially those that are called frequently with the same arguments. The decorator is versatile and works well with both simple and recursive functions, as well as handling keyword arguments.
import functools
@functools.lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10)) # Fast calculation after caching previous results
import functools
@functools.lru_cache(maxsize=3)
def compute(n):
return n * 2
compute(2)
compute(3)
compute(2)
print(compute.cache_info()) # Shows cache hits, misses, and current size
import functools
@functools.lru_cache(maxsize=2)
def add(x, y):
return x + y
add(2, 3)
add(4, 5)
print(add.cache_info()) # Shows cache hits/misses
add.cache_clear() # Clears the cache
print(add.cache_info()) # Cache info after clearing
import functools
@functools.lru_cache(maxsize=5)
def multiply(x, y):
return x * y
multiply(2, 3)
multiply(4, 5)
multiply(6, 7)
multiply(8, 9)
multiply(10, 11)
multiply(12, 13) # This call will evict the oldest cache entry
print(multiply.cache_info())
import functools
@functools.lru_cache(maxsize=3)
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice")) # First call, computed
print(greet("Alice")) # Cached result
print(greet("Bob", greeting="Hi")) # New result, not cached
import functools
@functools.lru_cache(maxsize=None)
def no_args():
print("Executing no_args function...")
return "Hello, world!"
print(no_args()) # First execution
print(no_args()) # Cached result
import functools
@functools.lru_cache(maxsize=2)
def process_data(data):
print("Processing data...")
return sum(data)
# List is mutable, so it cannot be used directly
# process_data([1, 2, 3]) # This would raise TypeError
# Use a tuple (immutable) instead
print(process_data((1, 2, 3))) # Cached result
print(process_data((4, 5, 6))) # Cached result
import functools
def cache_decorator(func):
cached_func = functools.lru_cache(maxsize=2)(func)
return cached_func
@cache_decorator
def square(n):
print(f"Squaring {n}")
return n * n
print(square(2)) # First call
print(square(3)) # First call
print(square(2)) # Cached
print(square(4)) # New calculation, old cache entry evicted