10
10
11
11
class DoubleLinkedListNode :
12
12
"""Node for LRU Cache"""
13
-
13
+
14
14
__slots__ = ("key" , "val" , "next" , "prev" )
15
-
15
+
16
16
def __init__ (self , key : Any , val : Any ) -> None :
17
17
self .key = key
18
18
self .val = val
19
19
self .next : DoubleLinkedListNode | None = None
20
20
self .prev : DoubleLinkedListNode | None = None
21
-
21
+
22
22
def __repr__ (self ) -> str :
23
23
return f"Node(key={ self .key } , val={ self .val } )"
24
24
25
25
26
26
class DoubleLinkedList :
27
27
"""Double Linked List for LRU Cache"""
28
-
28
+
29
29
def __init__ (self ) -> None :
30
30
# Create sentinel nodes
31
31
self .head = DoubleLinkedListNode (None , None )
32
32
self .rear = DoubleLinkedListNode (None , None )
33
33
# Link sentinel nodes together
34
34
self .head .next = self .rear
35
35
self .rear .prev = self .head
36
-
36
+
37
37
def __repr__ (self ) -> str :
38
38
nodes = []
39
39
current = self .head
40
40
while current :
41
41
nodes .append (repr (current ))
42
42
current = current .next
43
43
return f"LinkedList({ nodes } )"
44
-
44
+
45
45
def add (self , node : DoubleLinkedListNode ) -> None :
46
46
"""Add node before rear"""
47
47
prev = self .rear .prev
48
48
if prev is None :
49
49
return
50
-
50
+
51
51
# Insert node between prev and rear
52
52
prev .next = node
53
53
node .prev = prev
54
54
self .rear .prev = node
55
55
node .next = self .rear
56
-
56
+
57
57
def remove (self , node : DoubleLinkedListNode ) -> DoubleLinkedListNode | None :
58
58
"""Remove node from list"""
59
59
if node .prev is None or node .next is None :
60
60
return None
61
-
61
+
62
62
# Bypass node
63
63
node .prev .next = node .next
64
64
node .next .prev = node .prev
65
-
65
+
66
66
# Clear node references
67
67
node .prev = None
68
68
node .next = None
@@ -71,21 +71,21 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None:
71
71
72
72
class LRUCache :
73
73
"""LRU Cache implementation"""
74
-
74
+
75
75
def __init__ (self , capacity : int ) -> None :
76
76
self .list = DoubleLinkedList ()
77
77
self .capacity = capacity
78
78
self .size = 0
79
79
self .hits = 0
80
80
self .misses = 0
81
81
self .cache : dict [Any , DoubleLinkedListNode ] = {}
82
-
82
+
83
83
def __repr__ (self ) -> str :
84
84
return (
85
85
f"Cache(hits={ self .hits } , misses={ self .misses } , "
86
86
f"cap={ self .capacity } , size={ self .size } )"
87
87
)
88
-
88
+
89
89
def get (self , key : Any ) -> Any | None :
90
90
"""Get value for key"""
91
91
if key in self .cache :
@@ -96,7 +96,7 @@ def get(self, key: Any) -> Any | None:
96
96
return node .val
97
97
self .misses += 1
98
98
return None
99
-
99
+
100
100
def put (self , key : Any , value : Any ) -> None :
101
101
"""Set value for key"""
102
102
if key in self .cache :
@@ -106,58 +106,59 @@ def put(self, key: Any, value: Any) -> None:
106
106
node .val = value
107
107
self .list .add (node )
108
108
return
109
-
109
+
110
110
# Evict LRU item if at capacity
111
111
if self .size >= self .capacity :
112
112
first_node = self .list .head .next
113
113
if first_node and first_node .key and first_node != self .list .rear :
114
114
if self .list .remove (first_node ):
115
115
del self .cache [first_node .key ]
116
116
self .size -= 1
117
-
117
+
118
118
# Add new node
119
119
new_node = DoubleLinkedListNode (key , value )
120
120
self .cache [key ] = new_node
121
121
self .list .add (new_node )
122
122
self .size += 1
123
-
123
+
124
124
def cache_info (self ) -> dict [str , Any ]:
125
125
"""Get cache statistics"""
126
126
return {
127
127
"hits" : self .hits ,
128
128
"misses" : self .misses ,
129
129
"capacity" : self .capacity ,
130
- "size" : self .size
130
+ "size" : self .size ,
131
131
}
132
132
133
133
134
134
def lru_cache (maxsize : int = 128 ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
135
135
"""LRU Cache decorator"""
136
+
136
137
def decorator (func : Callable [P , R ]) -> Callable [P , R ]:
137
138
cache = LRUCache (maxsize )
138
-
139
+
139
140
@wraps (func )
140
141
def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> R :
141
142
# Create normalized cache key
142
143
key = (args , tuple (sorted (kwargs .items ())))
143
-
144
+
144
145
# Try to get cached result
145
- cached = cache .get (key )
146
- if cached is not None :
146
+ if (cached := cache .get (key )) is not None :
147
147
return cached
148
-
148
+
149
149
# Compute and cache result
150
150
result = func (* args , ** kwargs )
151
151
cache .put (key , result )
152
152
return result
153
-
153
+
154
154
# Attach cache info method
155
155
wrapper .cache_info = cache .cache_info # type: ignore[attr-defined]
156
156
return wrapper
157
-
157
+
158
158
return decorator
159
159
160
160
161
161
if __name__ == "__main__" :
162
162
import doctest
163
+
163
164
doctest .testmod ()
0 commit comments