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 :
4949 return # Should never happen with sentinel nodes
50-
50+
5151 prev .next = node
5252 node .prev = prev
5353 self .rear .prev = node
5454 node .next = self .rear
55-
55+
5656 def remove (self , node : DoubleLinkedListNode ) -> DoubleLinkedListNode | None :
5757 """Remove node from list"""
5858 if node .prev is None or node .next is None :
5959 return None
60-
60+
6161 node .prev .next = node .next
6262 node .next .prev = node .prev
6363 node .prev = node .next = None
@@ -66,21 +66,21 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None:
6666
6767class LRUCache :
6868 """LRU Cache implementation"""
69-
69+
7070 def __init__ (self , capacity : int ) -> None :
7171 self .list = DoubleLinkedList ()
7272 self .capacity = capacity
7373 self .size = 0
7474 self .hits = 0
7575 self .misses = 0
7676 self .cache : dict [Any , DoubleLinkedListNode ] = {}
77-
77+
7878 def __repr__ (self ) -> str :
7979 return (
8080 f"Cache(hits={ self .hits } , misses={ self .misses } , "
8181 f"cap={ self .capacity } , size={ self .size } )"
8282 )
83-
83+
8484 def get (self , key : Any ) -> Any | None :
8585 """Get value for key"""
8686 if key in self .cache :
@@ -91,7 +91,7 @@ def get(self, key: Any) -> Any | None:
9191 return node .val
9292 self .misses += 1
9393 return None
94-
94+
9595 def put (self , key : Any , value : Any ) -> None :
9696 """Set value for key"""
9797 if key in self .cache :
@@ -100,57 +100,58 @@ def put(self, key: Any, value: Any) -> None:
100100 node .val = value
101101 self .list .add (node )
102102 return
103-
103+
104104 if self .size >= self .capacity :
105105 # Remove least recently used item
106106 first_node = self .list .head .next
107107 if first_node and first_node != self .list .rear :
108108 if self .list .remove (first_node ):
109109 del self .cache [first_node .key ]
110110 self .size -= 1
111-
111+
112112 new_node = DoubleLinkedListNode (key , value )
113113 self .cache [key ] = new_node
114114 self .list .add (new_node )
115115 self .size += 1
116-
116+
117117 def cache_info (self ) -> dict [str , Any ]:
118118 """Get cache statistics"""
119119 return {
120120 "hits" : self .hits ,
121121 "misses" : self .misses ,
122122 "capacity" : self .capacity ,
123- "size" : self .size
123+ "size" : self .size ,
124124 }
125125
126126
127127def lru_cache (maxsize : int = 128 ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
128128 """LRU Cache decorator"""
129+
129130 def decorator (func : Callable [P , R ]) -> Callable [P , R ]:
130131 cache = LRUCache (maxsize )
131-
132+
132133 @wraps (func )
133134 def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> R :
134135 # Create normalized cache key
135136 key = (args , tuple (sorted (kwargs .items ())))
136-
137+
137138 # 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 :
140140 return cached
141-
141+
142142 # Compute and cache result
143143 result = func (* args , ** kwargs )
144144 cache .put (key , result )
145145 return result
146-
146+
147147 # Attach cache info method
148148 wrapper .cache_info = cache .cache_info # type: ignore[attr-defined]
149149 return wrapper
150-
150+
151151 return decorator
152152
153153
154154if __name__ == "__main__" :
155155 import doctest
156+
156157 doctest .testmod ()
0 commit comments