22
33from collections .abc import Callable , Hashable
44from functools import wraps
5- from typing import Any , ParamSpec , TypeVar , cast
5+ from typing import Any , ParamSpec , TypeVar
66
77P = ParamSpec ("P" )
88R = TypeVar ("R" )
99
1010
1111class DoubleLinkedListNode :
1212 """Node for LRU Cache"""
13-
13+
1414 __slots__ = ("key" , "val" , "next" , "prev" )
15-
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
2626class 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 :
49- return # Should never happen with sentinel nodes
50-
49+ return
50+
51+ # Insert node between prev and rear
5152 prev .next = node
5253 node .prev = prev
5354 self .rear .prev = node
5455 node .next = self .rear
55-
56+
5657 def remove (self , node : DoubleLinkedListNode ) -> DoubleLinkedListNode | None :
5758 """Remove node from list"""
5859 if node .prev is None or node .next is None :
5960 return None
60-
61+
62+ # Bypass node
6163 node .prev .next = node .next
6264 node .next .prev = node .prev
63- node .prev = node .next = None
65+
66+ # Clear node references
67+ node .prev = None
68+ node .next = None
6469 return node
6570
6671
6772class LRUCache :
6873 """LRU Cache implementation"""
69-
74+
7075 def __init__ (self , capacity : int ) -> None :
7176 self .list = DoubleLinkedList ()
7277 self .capacity = capacity
7378 self .size = 0
7479 self .hits = 0
7580 self .misses = 0
7681 self .cache : dict [Any , DoubleLinkedListNode ] = {}
77-
82+
7883 def __repr__ (self ) -> str :
7984 return (
8085 f"Cache(hits={ self .hits } , misses={ self .misses } , "
8186 f"cap={ self .capacity } , size={ self .size } )"
8287 )
83-
88+
8489 def get (self , key : Any ) -> Any | None :
8590 """Get value for key"""
8691 if key in self .cache :
@@ -91,67 +96,68 @@ def get(self, key: Any) -> Any | None:
9196 return node .val
9297 self .misses += 1
9398 return None
94-
99+
95100 def put (self , key : Any , value : Any ) -> None :
96101 """Set value for key"""
97102 if key in self .cache :
103+ # Update existing node
98104 node = self .cache [key ]
99105 if self .list .remove (node ):
100106 node .val = value
101107 self .list .add (node )
102108 return
103-
109+
110+ # Evict LRU item if at capacity
104111 if self .size >= self .capacity :
105- # Remove least recently used item
106112 first_node = self .list .head .next
107- if first_node and first_node != self .list .rear :
113+ if first_node and first_node . key and first_node != self .list .rear :
108114 if self .list .remove (first_node ):
109115 del self .cache [first_node .key ]
110116 self .size -= 1
111-
117+
118+ # Add new node
112119 new_node = DoubleLinkedListNode (key , value )
113120 self .cache [key ] = new_node
114121 self .list .add (new_node )
115122 self .size += 1
116-
123+
117124 def cache_info (self ) -> dict [str , Any ]:
118125 """Get cache statistics"""
119126 return {
120127 "hits" : self .hits ,
121128 "misses" : self .misses ,
122129 "capacity" : self .capacity ,
123- "size" : self .size ,
130+ "size" : self .size
124131 }
125132
126133
127134def lru_cache (maxsize : int = 128 ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
128135 """LRU Cache decorator"""
129-
130136 def decorator (func : Callable [P , R ]) -> Callable [P , R ]:
131137 cache = LRUCache (maxsize )
132-
138+
133139 @wraps (func )
134140 def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> R :
135141 # Create normalized cache key
136142 key = (args , tuple (sorted (kwargs .items ())))
137-
143+
138144 # Try to get cached result
139- if (cached := cache .get (key )) is not None :
145+ cached = cache .get (key )
146+ if cached is not None :
140147 return cached
141-
148+
142149 # Compute and cache result
143150 result = func (* args , ** kwargs )
144151 cache .put (key , result )
145152 return result
146-
153+
147154 # Attach cache info method
148155 wrapper .cache_info = cache .cache_info # type: ignore[attr-defined]
149156 return wrapper
150-
157+
151158 return decorator
152159
153160
154161if __name__ == "__main__" :
155162 import doctest
156-
157163 doctest .testmod ()
0 commit comments