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 # Should never happen with sentinel nodes
50
-
50
+
51
51
prev .next = node
52
52
node .prev = prev
53
53
self .rear .prev = node
54
54
node .next = self .rear
55
-
55
+
56
56
def remove (self , node : DoubleLinkedListNode ) -> DoubleLinkedListNode | None :
57
57
"""Remove node from list"""
58
58
if node .prev is None or node .next is None :
59
59
return None
60
-
60
+
61
61
node .prev .next = node .next
62
62
node .next .prev = node .prev
63
63
node .prev = node .next = None
@@ -66,21 +66,21 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None:
66
66
67
67
class LRUCache :
68
68
"""LRU Cache implementation"""
69
-
69
+
70
70
def __init__ (self , capacity : int ) -> None :
71
71
self .list = DoubleLinkedList ()
72
72
self .capacity = capacity
73
73
self .size = 0
74
74
self .hits = 0
75
75
self .misses = 0
76
76
self .cache : dict [Any , DoubleLinkedListNode ] = {}
77
-
77
+
78
78
def __repr__ (self ) -> str :
79
79
return (
80
80
f"Cache(hits={ self .hits } , misses={ self .misses } , "
81
81
f"cap={ self .capacity } , size={ self .size } )"
82
82
)
83
-
83
+
84
84
def get (self , key : Any ) -> Any | None :
85
85
"""Get value for key"""
86
86
if key in self .cache :
@@ -91,7 +91,7 @@ def get(self, key: Any) -> Any | None:
91
91
return node .val
92
92
self .misses += 1
93
93
return None
94
-
94
+
95
95
def put (self , key : Any , value : Any ) -> None :
96
96
"""Set value for key"""
97
97
if key in self .cache :
@@ -100,57 +100,58 @@ def put(self, key: Any, value: Any) -> None:
100
100
node .val = value
101
101
self .list .add (node )
102
102
return
103
-
103
+
104
104
if self .size >= self .capacity :
105
105
# Remove least recently used item
106
106
first_node = self .list .head .next
107
107
if first_node and first_node != self .list .rear :
108
108
if self .list .remove (first_node ):
109
109
del self .cache [first_node .key ]
110
110
self .size -= 1
111
-
111
+
112
112
new_node = DoubleLinkedListNode (key , value )
113
113
self .cache [key ] = new_node
114
114
self .list .add (new_node )
115
115
self .size += 1
116
-
116
+
117
117
def cache_info (self ) -> dict [str , Any ]:
118
118
"""Get cache statistics"""
119
119
return {
120
120
"hits" : self .hits ,
121
121
"misses" : self .misses ,
122
122
"capacity" : self .capacity ,
123
- "size" : self .size
123
+ "size" : self .size ,
124
124
}
125
125
126
126
127
127
def lru_cache (maxsize : int = 128 ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
128
128
"""LRU Cache decorator"""
129
+
129
130
def decorator (func : Callable [P , R ]) -> Callable [P , R ]:
130
131
cache = LRUCache (maxsize )
131
-
132
+
132
133
@wraps (func )
133
134
def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> R :
134
135
# Create normalized cache key
135
136
key = (args , tuple (sorted (kwargs .items ())))
136
-
137
+
137
138
# Try to get cached result
138
- cached = cache .get (key )
139
- if cached is not None :
139
+ if (cached := cache .get (key )) is not None :
140
140
return cached
141
-
141
+
142
142
# Compute and cache result
143
143
result = func (* args , ** kwargs )
144
144
cache .put (key , result )
145
145
return result
146
-
146
+
147
147
# Attach cache info method
148
148
wrapper .cache_info = cache .cache_info # type: ignore[attr-defined]
149
149
return wrapper
150
-
150
+
151
151
return decorator
152
152
153
153
154
154
if __name__ == "__main__" :
155
155
import doctest
156
+
156
157
doctest .testmod ()
0 commit comments