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
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,58 +106,59 @@ 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
113113 if first_node and first_node .key and first_node != self .list .rear :
114114 if self .list .remove (first_node ):
115115 del self .cache [first_node .key ]
116116 self .size -= 1
117-
117+
118118 # Add new node
119119 new_node = DoubleLinkedListNode (key , value )
120120 self .cache [key ] = new_node
121121 self .list .add (new_node )
122122 self .size += 1
123-
123+
124124 def cache_info (self ) -> dict [str , Any ]:
125125 """Get cache statistics"""
126126 return {
127127 "hits" : self .hits ,
128128 "misses" : self .misses ,
129129 "capacity" : self .capacity ,
130- "size" : self .size
130+ "size" : self .size ,
131131 }
132132
133133
134134def lru_cache (maxsize : int = 128 ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
135135 """LRU Cache decorator"""
136+
136137 def decorator (func : Callable [P , R ]) -> Callable [P , R ]:
137138 cache = LRUCache (maxsize )
138-
139+
139140 @wraps (func )
140141 def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> R :
141142 # Create normalized cache key
142143 key = (args , tuple (sorted (kwargs .items ())))
143-
144+
144145 # Try to get cached result
145- cached = cache .get (key )
146- if cached is not None :
146+ if (cached := cache .get (key )) is not None :
147147 return cached
148-
148+
149149 # Compute and cache result
150150 result = func (* args , ** kwargs )
151151 cache .put (key , result )
152152 return result
153-
153+
154154 # Attach cache info method
155155 wrapper .cache_info = cache .cache_info # type: ignore[attr-defined]
156156 return wrapper
157-
157+
158158 return decorator
159159
160160
161161if __name__ == "__main__" :
162162 import doctest
163+
163164 doctest .testmod ()
0 commit comments