11from __future__ import annotations
22
3- from collections .abc import Callable , Hashable
3+ from collections .abc import Callable
44from functools import wraps
55from typing import Any , ParamSpec , TypeVar
66
1010
1111class DoubleLinkedListNode :
1212 """Node for LRU Cache"""
13-
14- __slots__ = ("key" , "val " , "next " , "prev " )
15-
13+
14+ __slots__ = ("key" , "next " , "prev " , "val " )
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,59 +106,62 @@ 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
113- if first_node and first_node .key and first_node != self .list .rear :
114- if self .list .remove (first_node ):
115- del self .cache [first_node .key ]
116- self .size -= 1
117-
113+ if (
114+ first_node
115+ and first_node .key is not None
116+ and first_node != self .list .rear
117+ and self .list .remove (first_node )
118+ ):
119+ del self .cache [first_node .key ]
120+ self .size -= 1
121+
118122 # Add new node
119123 new_node = DoubleLinkedListNode (key , value )
120124 self .cache [key ] = new_node
121125 self .list .add (new_node )
122126 self .size += 1
123-
127+
124128 def cache_info (self ) -> dict [str , Any ]:
125129 """Get cache statistics"""
126130 return {
127131 "hits" : self .hits ,
128132 "misses" : self .misses ,
129133 "capacity" : self .capacity ,
130- "size" : self .size ,
134+ "size" : self .size
131135 }
132136
133137
134138def lru_cache (maxsize : int = 128 ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
135139 """LRU Cache decorator"""
136-
137140 def decorator (func : Callable [P , R ]) -> Callable [P , R ]:
138141 cache = LRUCache (maxsize )
139-
142+
140143 @wraps (func )
141144 def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> R :
142145 # Create normalized cache key
143146 key = (args , tuple (sorted (kwargs .items ())))
144-
147+
145148 # Try to get cached result
146- if (cached := cache .get (key )) is not None :
149+ cached = cache .get (key )
150+ if cached is not None :
147151 return cached
148-
152+
149153 # Compute and cache result
150154 result = func (* args , ** kwargs )
151155 cache .put (key , result )
152156 return result
153-
157+
154158 # Attach cache info method
155159 wrapper .cache_info = cache .cache_info # type: ignore[attr-defined]
156160 return wrapper
157-
161+
158162 return decorator
159163
160164
161165if __name__ == "__main__" :
162166 import doctest
163-
164167 doctest .testmod ()
0 commit comments