Skip to content

Commit 0a2396c

Browse files
authored
Update lru_cache.py
1 parent 98991c8 commit 0a2396c

File tree

1 file changed

+35
-32
lines changed

1 file changed

+35
-32
lines changed

other/lru_cache.py

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from collections.abc import Callable, Hashable
3+
from collections.abc import Callable
44
from functools import wraps
55
from typing import Any, ParamSpec, TypeVar
66

@@ -10,59 +10,59 @@
1010

1111
class DoubleLinkedListNode:
1212
"""Node for LRU Cache"""
13-
14-
__slots__ = ("key", "val", "next", "prev")
15-
13+
14+
__slots__ = ("key", "next", "prev", "val")
15+
1616
def __init__(self, key: Any, val: Any) -> None:
1717
self.key = key
1818
self.val = val
1919
self.next: DoubleLinkedListNode | None = None
2020
self.prev: DoubleLinkedListNode | None = None
21-
21+
2222
def __repr__(self) -> str:
2323
return f"Node(key={self.key}, val={self.val})"
2424

2525

2626
class DoubleLinkedList:
2727
"""Double Linked List for LRU Cache"""
28-
28+
2929
def __init__(self) -> None:
3030
# Create sentinel nodes
3131
self.head = DoubleLinkedListNode(None, None)
3232
self.rear = DoubleLinkedListNode(None, None)
3333
# Link sentinel nodes together
3434
self.head.next = self.rear
3535
self.rear.prev = self.head
36-
36+
3737
def __repr__(self) -> str:
3838
nodes = []
3939
current = self.head
4040
while current:
4141
nodes.append(repr(current))
4242
current = current.next
4343
return f"LinkedList({nodes})"
44-
44+
4545
def add(self, node: DoubleLinkedListNode) -> None:
4646
"""Add node before rear"""
4747
prev = self.rear.prev
4848
if prev is None:
4949
return
50-
50+
5151
# Insert node between prev and rear
5252
prev.next = node
5353
node.prev = prev
5454
self.rear.prev = node
5555
node.next = self.rear
56-
56+
5757
def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None:
5858
"""Remove node from list"""
5959
if node.prev is None or node.next is None:
6060
return None
61-
61+
6262
# Bypass node
6363
node.prev.next = node.next
6464
node.next.prev = node.prev
65-
65+
6666
# Clear node references
6767
node.prev = None
6868
node.next = None
@@ -71,21 +71,21 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None:
7171

7272
class LRUCache:
7373
"""LRU Cache implementation"""
74-
74+
7575
def __init__(self, capacity: int) -> None:
7676
self.list = DoubleLinkedList()
7777
self.capacity = capacity
7878
self.size = 0
7979
self.hits = 0
8080
self.misses = 0
8181
self.cache: dict[Any, DoubleLinkedListNode] = {}
82-
82+
8383
def __repr__(self) -> str:
8484
return (
8585
f"Cache(hits={self.hits}, misses={self.misses}, "
8686
f"cap={self.capacity}, size={self.size})"
8787
)
88-
88+
8989
def get(self, key: Any) -> Any | None:
9090
"""Get value for key"""
9191
if key in self.cache:
@@ -96,7 +96,7 @@ def get(self, key: Any) -> Any | None:
9696
return node.val
9797
self.misses += 1
9898
return None
99-
99+
100100
def put(self, key: Any, value: Any) -> None:
101101
"""Set value for key"""
102102
if key in self.cache:
@@ -106,59 +106,62 @@ def put(self, key: Any, value: Any) -> None:
106106
node.val = value
107107
self.list.add(node)
108108
return
109-
109+
110110
# Evict LRU item if at capacity
111111
if self.size >= self.capacity:
112112
first_node = self.list.head.next
113-
if first_node and first_node.key and first_node != self.list.rear:
114-
if self.list.remove(first_node):
115-
del self.cache[first_node.key]
116-
self.size -= 1
117-
113+
if (
114+
first_node
115+
and first_node.key is not None
116+
and first_node != self.list.rear
117+
and self.list.remove(first_node)
118+
):
119+
del self.cache[first_node.key]
120+
self.size -= 1
121+
118122
# Add new node
119123
new_node = DoubleLinkedListNode(key, value)
120124
self.cache[key] = new_node
121125
self.list.add(new_node)
122126
self.size += 1
123-
127+
124128
def cache_info(self) -> dict[str, Any]:
125129
"""Get cache statistics"""
126130
return {
127131
"hits": self.hits,
128132
"misses": self.misses,
129133
"capacity": self.capacity,
130-
"size": self.size,
134+
"size": self.size
131135
}
132136

133137

134138
def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]:
135139
"""LRU Cache decorator"""
136-
137140
def decorator(func: Callable[P, R]) -> Callable[P, R]:
138141
cache = LRUCache(maxsize)
139-
142+
140143
@wraps(func)
141144
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
142145
# Create normalized cache key
143146
key = (args, tuple(sorted(kwargs.items())))
144-
147+
145148
# Try to get cached result
146-
if (cached := cache.get(key)) is not None:
149+
cached = cache.get(key)
150+
if cached is not None:
147151
return cached
148-
152+
149153
# Compute and cache result
150154
result = func(*args, **kwargs)
151155
cache.put(key, result)
152156
return result
153-
157+
154158
# Attach cache info method
155159
wrapper.cache_info = cache.cache_info # type: ignore[attr-defined]
156160
return wrapper
157-
161+
158162
return decorator
159163

160164

161165
if __name__ == "__main__":
162166
import doctest
163-
164167
doctest.testmod()

0 commit comments

Comments
 (0)