1
1
from __future__ import annotations
2
2
3
- from typing import Callable
3
+ from typing import Callable , Generic , TypeVar
4
4
5
+ T = TypeVar ("T" )
6
+ U = TypeVar ("U" )
5
7
6
- class DoubleLinkedListNode :
8
+
9
+ class DoubleLinkedListNode (Generic [T , U ]):
7
10
"""
8
11
Double Linked List Node built specifically for LRU Cache
9
12
@@ -12,19 +15,19 @@ class DoubleLinkedListNode:
12
15
Node: key: 1, val: 1, has next: False, has prev: False
13
16
"""
14
17
15
- def __init__ (self , key : int | None , val : int | None ):
18
+ def __init__ (self , key : T | None , val : U | None ):
16
19
self .key = key
17
20
self .val = val
18
- self .next : DoubleLinkedListNode | None = None
19
- self .prev : DoubleLinkedListNode | None = None
21
+ self .next : DoubleLinkedListNode [ T , U ] | None = None
22
+ self .prev : DoubleLinkedListNode [ T , U ] | None = None
20
23
21
24
def __repr__ (self ) -> str :
22
25
return "Node: key: {}, val: {}, has next: {}, has prev: {}" .format (
23
26
self .key , self .val , self .next is not None , self .prev is not None
24
27
)
25
28
26
29
27
- class DoubleLinkedList :
30
+ class DoubleLinkedList ( Generic [ T , U ]) :
28
31
"""
29
32
Double Linked List built specifically for LRU Cache
30
33
@@ -92,8 +95,8 @@ class DoubleLinkedList:
92
95
"""
93
96
94
97
def __init__ (self ) -> None :
95
- self .head = DoubleLinkedListNode (None , None )
96
- self .rear = DoubleLinkedListNode (None , None )
98
+ self .head : DoubleLinkedListNode [ T , U ] = DoubleLinkedListNode (None , None )
99
+ self .rear : DoubleLinkedListNode [ T , U ] = DoubleLinkedListNode (None , None )
97
100
self .head .next , self .rear .prev = self .rear , self .head
98
101
99
102
def __repr__ (self ) -> str :
@@ -105,7 +108,7 @@ def __repr__(self) -> str:
105
108
rep .append (str (self .rear ))
106
109
return ",\n " .join (rep )
107
110
108
- def add (self , node : DoubleLinkedListNode ) -> None :
111
+ def add (self , node : DoubleLinkedListNode [ T , U ] ) -> None :
109
112
"""
110
113
Adds the given node to the end of the list (before rear)
111
114
"""
@@ -120,7 +123,9 @@ def add(self, node: DoubleLinkedListNode) -> None:
120
123
self .rear .prev = node
121
124
node .next = self .rear
122
125
123
- def remove (self , node : DoubleLinkedListNode ) -> DoubleLinkedListNode | None :
126
+ def remove (
127
+ self , node : DoubleLinkedListNode [T , U ]
128
+ ) -> DoubleLinkedListNode [T , U ] | None :
124
129
"""
125
130
Removes and returns the given node from the list
126
131
@@ -140,8 +145,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None:
140
145
return node
141
146
142
147
143
- # class LRUCache(Generic[T]):
144
- class LRUCache :
148
+ class LRUCache (Generic [T , U ]):
145
149
"""
146
150
LRU Cache to store a given capacity of data. Can be used as a stand-alone object
147
151
or as a function decorator.
@@ -206,17 +210,15 @@ class LRUCache:
206
210
"""
207
211
208
212
# class variable to map the decorator functions to their respective instance
209
- decorator_function_to_instance_map : dict [Callable , LRUCache ] = {}
213
+ decorator_function_to_instance_map : dict [Callable [[ T ], U ], LRUCache [ T , U ] ] = {}
210
214
211
215
def __init__ (self , capacity : int ):
212
- self .list = DoubleLinkedList ()
216
+ self .list : DoubleLinkedList [ T , U ] = DoubleLinkedList ()
213
217
self .capacity = capacity
214
218
self .num_keys = 0
215
219
self .hits = 0
216
220
self .miss = 0
217
- # self.cache: dict[int, int] = {}
218
- # self.cache: dict[int, T] = {}
219
- self .cache : dict [int , DoubleLinkedListNode ] = {}
221
+ self .cache : dict [T , DoubleLinkedListNode [T , U ]] = {}
220
222
221
223
def __repr__ (self ) -> str :
222
224
"""
@@ -229,7 +231,7 @@ def __repr__(self) -> str:
229
231
f"capacity={ self .capacity } , current size={ self .num_keys } )"
230
232
)
231
233
232
- def __contains__ (self , key : int ) -> bool :
234
+ def __contains__ (self , key : T ) -> bool :
233
235
"""
234
236
>>> cache = LRUCache(1)
235
237
@@ -244,15 +246,15 @@ def __contains__(self, key: int) -> bool:
244
246
245
247
return key in self .cache
246
248
247
- def get (self , key : int ) -> int | None :
249
+ def get (self , key : T ) -> U | None :
248
250
"""
249
251
Returns the value for the input key and updates the Double Linked List.
250
252
Returns None if key is not present in cache
251
253
"""
252
254
253
255
if key in self .cache :
254
256
self .hits += 1
255
- value_node = self .cache [key ]
257
+ value_node : DoubleLinkedListNode [ T , U ] = self .cache [key ]
256
258
node = self .list .remove (self .cache [key ])
257
259
assert node == value_node
258
260
@@ -263,7 +265,7 @@ def get(self, key: int) -> int | None:
263
265
self .miss += 1
264
266
return None
265
267
266
- def set (self , key : int , value : int ) -> None :
268
+ def set (self , key : T , value : U ) -> None :
267
269
"""
268
270
Sets the value for the input key and updates the Double Linked List
269
271
"""
@@ -277,7 +279,9 @@ def set(self, key: int, value: int) -> None:
277
279
# explain to type checker via assertions
278
280
assert first_node is not None
279
281
assert first_node .key is not None
280
- assert self .list .remove (first_node ) is not None # node guaranteed to be in list assert node.key is not None
282
+ assert (
283
+ self .list .remove (first_node ) is not None
284
+ ) # node guaranteed to be in list assert node.key is not None
281
285
282
286
del self .cache [first_node .key ]
283
287
self .num_keys -= 1
@@ -293,14 +297,15 @@ def set(self, key: int, value: int) -> None:
293
297
self .list .add (node )
294
298
295
299
@staticmethod
296
- def decorator (size : int = 128 ) -> Callable [[Callable [[int ], int ]], Callable [..., int ]]:
300
+ def decorator (size : int = 128 ) -> Callable [[Callable [[T ], U ]], Callable [..., U ]]:
297
301
"""
298
302
Decorator version of LRU Cache
299
303
300
- Decorated function must be function of int -> int
304
+ Decorated function must be function of T -> U
301
305
"""
302
- def cache_decorator_inner (func : Callable [[int ], int ]) -> Callable [..., int ]:
303
- def cache_decorator_wrapper (* args : int ) -> int :
306
+
307
+ def cache_decorator_inner (func : Callable [[T ], U ]) -> Callable [..., U ]:
308
+ def cache_decorator_wrapper (* args : T ) -> U :
304
309
if func not in LRUCache .decorator_function_to_instance_map :
305
310
LRUCache .decorator_function_to_instance_map [func ] = LRUCache (size )
306
311
0 commit comments