22
33from collections .abc import Callable , Hashable
44from functools import wraps
5- from typing import Any , Generic , ParamSpec , TypeVar , cast
5+ from typing import Any , ParamSpec , TypeVar , cast
66
77P = ParamSpec ("P" )
88R = TypeVar ("R" )
99
1010
1111class DoubleLinkedListNode :
1212 """Node for LRU Cache"""
13-
14- def __init__ (self , key : Any | None , val : Any | None ) -> None :
13+
14+ __slots__ = ("key" , "val" , "next" , "prev" )
15+
16+ def __init__ (self , key : Any , val : Any ) -> None :
1517 self .key = key
1618 self .val = val
1719 self .next : DoubleLinkedListNode | None = None
1820 self .prev : DoubleLinkedListNode | None = None
19-
21+
2022 def __repr__ (self ) -> str :
2123 return f"Node(key={ self .key } , val={ self .val } )"
2224
2325
2426class DoubleLinkedList :
2527 """Double Linked List for LRU Cache"""
26-
28+
2729 def __init__ (self ) -> None :
30+ # Create sentinel nodes
2831 self .head = DoubleLinkedListNode (None , None )
2932 self .rear = DoubleLinkedListNode (None , None )
33+ # Link sentinel nodes together
3034 self .head .next = self .rear
3135 self .rear .prev = self .head
32-
36+
3337 def __repr__ (self ) -> str :
3438 nodes = []
3539 current = self .head
3640 while current :
3741 nodes .append (repr (current ))
3842 current = current .next
3943 return f"LinkedList({ nodes } )"
40-
44+
4145 def add (self , node : DoubleLinkedListNode ) -> None :
42- """Add node to list end """
46+ """Add node before rear """
4347 prev = self .rear .prev
4448 if prev is None :
45- raise ValueError ( "Invalid list state" )
46-
49+ return # Should never happen with sentinel nodes
50+
4751 prev .next = node
4852 node .prev = prev
4953 self .rear .prev = node
5054 node .next = self .rear
51-
55+
5256 def remove (self , node : DoubleLinkedListNode ) -> DoubleLinkedListNode | None :
5357 """Remove node from list"""
5458 if node .prev is None or node .next is None :
5559 return None
56-
60+
5761 node .prev .next = node .next
5862 node .next .prev = node .prev
5963 node .prev = node .next = None
@@ -62,21 +66,21 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None:
6266
6367class LRUCache :
6468 """LRU Cache implementation"""
65-
69+
6670 def __init__ (self , capacity : int ) -> None :
6771 self .list = DoubleLinkedList ()
6872 self .capacity = capacity
6973 self .size = 0
7074 self .hits = 0
7175 self .misses = 0
7276 self .cache : dict [Any , DoubleLinkedListNode ] = {}
73-
77+
7478 def __repr__ (self ) -> str :
7579 return (
7680 f"Cache(hits={ self .hits } , misses={ self .misses } , "
7781 f"cap={ self .capacity } , size={ self .size } )"
7882 )
79-
83+
8084 def get (self , key : Any ) -> Any | None :
8185 """Get value for key"""
8286 if key in self .cache :
@@ -87,7 +91,7 @@ def get(self, key: Any) -> Any | None:
8791 return node .val
8892 self .misses += 1
8993 return None
90-
94+
9195 def put (self , key : Any , value : Any ) -> None :
9296 """Set value for key"""
9397 if key in self .cache :
@@ -96,43 +100,57 @@ def put(self, key: Any, value: Any) -> None:
96100 node .val = value
97101 self .list .add (node )
98102 return
99-
103+
100104 if self .size >= self .capacity :
101- first = self .list .head .next
102- if first and first .key and self .list .remove (first ):
103- del self .cache [first .key ]
104- self .size -= 1
105-
105+ # Remove least recently used item
106+ first_node = self .list .head .next
107+ if first_node and first_node != self .list .rear :
108+ if self .list .remove (first_node ):
109+ del self .cache [first_node .key ]
110+ self .size -= 1
111+
106112 new_node = DoubleLinkedListNode (key , value )
107113 self .cache [key ] = new_node
108114 self .list .add (new_node )
109115 self .size += 1
110-
111-
112- def lru_cache (size : int = 128 ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
116+
117+ def cache_info (self ) -> dict [str , Any ]:
118+ """Get cache statistics"""
119+ return {
120+ "hits" : self .hits ,
121+ "misses" : self .misses ,
122+ "capacity" : self .capacity ,
123+ "size" : self .size
124+ }
125+
126+
127+ def lru_cache (maxsize : int = 128 ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
113128 """LRU Cache decorator"""
114-
115- def decorator_func (func : Callable [P , R ]) -> Callable [P , R ]:
116- cache = LRUCache (size )
117-
129+ def decorator (func : Callable [P , R ]) -> Callable [P , R ]:
130+ cache = LRUCache (maxsize )
131+
118132 @wraps (func )
119133 def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> R :
134+ # Create normalized cache key
120135 key = (args , tuple (sorted (kwargs .items ())))
121- if (cached := cache .get (key )) is not None :
136+
137+ # Try to get cached result
138+ cached = cache .get (key )
139+ if cached is not None :
122140 return cached
123-
141+
142+ # Compute and cache result
124143 result = func (* args , ** kwargs )
125144 cache .put (key , result )
126145 return result
127-
128- # Add cache_info attribute
129- wrapper .cache_info = lambda : cache # type: ignore[attr-defined]
146+
147+ # Attach cache info method
148+ wrapper .cache_info = cache . cache_info # type: ignore[attr-defined]
130149 return wrapper
131-
132- return decorator_func
150+
151+ return decorator
133152
134153
135154if __name__ == "__main__" :
136155 import doctest
137-
138156 doctest .testmod ()
0 commit comments