1010
1111class DoubleLinkedListNode :
1212 """Node for LRU Cache"""
13-
13+
1414 __slots__ = ("key" , "next" , "prev" , "val" )
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 :
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
7272class 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,7 +106,7 @@ 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
@@ -118,50 +118,51 @@ def put(self, key: Any, value: Any) -> None:
118118 ):
119119 del self .cache [first_node .key ]
120120 self .size -= 1
121-
121+
122122 # Add new node
123123 new_node = DoubleLinkedListNode (key , value )
124124 self .cache [key ] = new_node
125125 self .list .add (new_node )
126126 self .size += 1
127-
127+
128128 def cache_info (self ) -> dict [str , Any ]:
129129 """Get cache statistics"""
130130 return {
131131 "hits" : self .hits ,
132132 "misses" : self .misses ,
133133 "capacity" : self .capacity ,
134- "size" : self .size
134+ "size" : self .size ,
135135 }
136136
137137
138138def lru_cache (maxsize : int = 128 ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
139139 """LRU Cache decorator"""
140+
140141 def decorator (func : Callable [P , R ]) -> Callable [P , R ]:
141142 cache = LRUCache (maxsize )
142-
143+
143144 @wraps (func )
144145 def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> R :
145146 # Create normalized cache key
146147 key = (args , tuple (sorted (kwargs .items ())))
147-
148+
148149 # Try to get cached result
149- cached = cache .get (key )
150- if cached is not None :
150+ if (cached := cache .get (key )) is not None :
151151 return cast (R , cached )
152-
152+
153153 # Compute and cache result
154154 result = func (* args , ** kwargs )
155155 cache .put (key , result )
156156 return result
157-
157+
158158 # Attach cache info method
159159 wrapper .cache_info = cache .cache_info # type: ignore[attr-defined]
160160 return wrapper
161-
161+
162162 return decorator
163163
164164
165165if __name__ == "__main__" :
166166 import doctest
167+
167168 doctest .testmod ()
0 commit comments