{"id":7582,"library":"pylru","title":"PyLRU: Least Recently Used (LRU) Cache","description":"Pylru is a pure Python library implementing a Least Recently Used (LRU) cache. It provides a simple dictionary-like interface for `lrucache` and includes classes for wrapping existing dictionary-like objects (`WriteThroughCacheManager`) and functions (`FunctionCacheManager`, `lrudecorator`). The library is efficient, offering constant-time basic operations (lookup, insert, delete). The current version is 1.3.1, with releases happening periodically based on maintenance needs.","status":"active","version":"1.3.1","language":"en","source_language":"en","source_url":"https://github.com/jlhutch/pylru","tags":["cache","lru","memoization","performance"],"install":[{"cmd":"pip install pylru","lang":"bash","label":"Install with pip"}],"dependencies":[],"imports":[{"symbol":"lrucache","correct":"from pylru import lrucache"},{"symbol":"lrudecorator","correct":"from pylru import lrudecorator"},{"symbol":"WriteThroughCacheManager","correct":"from pylru import WriteThroughCacheManager"},{"symbol":"FunctionCacheManager","correct":"from pylru import FunctionCacheManager"},{"note":"A factory function for WriteThroughCacheManager.","symbol":"lruwrap","correct":"from pylru import lruwrap"}],"quickstart":{"code":"import pylru\n\n# Create an LRU cache with a maximum size of 3\ncache_size = 3\ncache = pylru.lrucache(cache_size)\n\n# Insert items into the cache\ncache['apple'] = 1\ncache['banana'] = 2\ncache['cherry'] = 3\nprint(f\"Cache after initial inserts: {list(cache.items())}\")\n\n# Access 'apple' - it becomes the most recently used\nprint(f\"Accessed 'apple': {cache['apple']}\")\nprint(f\"Cache order after accessing 'apple': {list(cache.keys())}\")\n\n# Insert a new item - 'banana' (least recently used) should be evicted\ncache['date'] = 4\nprint(f\"Cache after adding 'date': {list(cache.items())}\")\n\n# Test for membership\nprint(f\"Is 'cherry' in cache? {'cherry' in cache}\")\nprint(f\"Is 'banana' in cache? {'banana' in cache}\")\n\n# Using the lrudecorator\n@pylru.lrudecorator(max_size=2)\ndef fibonacci(n):\n    print(f\"Computing fibonacci({n})...\")\n    if n <= 1:\n        return n\n    return fibonacci(n - 1) + fibonacci(n - 2)\n\nprint(\"\\n--- Decorator Example ---\")\nprint(f\"Fib(3): {fibonacci(3)}\") # Computes\nprint(f\"Fib(2): {fibonacci(2)}\") # Computes\nprint(f\"Fib(3): {fibonacci(3)}\") # From cache (most recent)\nprint(f\"Fib(4): {fibonacci(4)}\") # Computes, fib(2) gets evicted as LRU","lang":"python","description":"This example demonstrates the basic usage of `pylru.lrucache` as a dictionary-like object, showing insertions, lookups, and the LRU eviction policy. It also includes a basic example of the `pylru.lrudecorator` for memoizing function calls."},"warnings":[{"fix":"Iterate over a copy of the keys/items: `for key in list(cache.keys()): ...`","message":"Modifying a pylru cache (insert, lookup, delete) during iteration (e.g., in a `for` loop) can lead to undefined behavior as it changes the internal order. If iteration is needed while modification might occur, convert `cache.keys()` or `cache.items()` to a `list` first.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure function arguments are immutable (numbers, strings, tuples) or convert mutable inputs to immutable forms (e.g., `tuple(list_arg)`) before passing them to a decorated function.","message":"The `@lrudecorator` only works reliably with functions whose arguments are hashable and whose equality is determined by the argument values, not their identity or mutable state. Passing mutable objects (lists, dictionaries, custom objects without proper `__hash__` and `__eq__` implementations) as arguments can lead to cache misses or stale data.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Minimize frequent calls to `len()` on `WriteThroughCacheManager` instances if performance is critical. Consider if the exact count is always necessary or if an approximation suffices.","message":"Calling `len(cached)` on a `WriteThroughCacheManager` instance can trigger an internal `sync()` operation, which might have adverse performance effects if called frequently, depending on the underlying store object.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Use `value = cache.get('key', default_value)` to provide a fallback, or check for key existence before access: `if 'key' in cache: value = cache['key']`.","cause":"Attempting to access a key that does not exist in the LRU cache using dictionary-style bracket notation (e.g., `cache['key']`).","error":"KeyError: 'non_existent_key'"},{"fix":"Refactor the function to accept only immutable arguments (e.g., numbers, strings, tuples of immutable types). If mutable data must be used, convert it to an immutable representation (e.g., a tuple of its contents) before passing it to the decorated function.","cause":"This often occurs when mutable objects are passed as arguments to a function decorated with `@lrudecorator`. The cache keys are based on the hash of the arguments, and while the object's identity (and thus hash) might remain the same, its internal state might change, making the cached result stale or causing it to be treated as a new entry if the hash changes.","error":"Decorated function re-computes results even for same inputs."}]}