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,7 @@ 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 (self , node : DoubleLinkedListNode [ T , U ] ) -> DoubleLinkedListNode [ T , U ] | None :
124
127
"""
125
128
Removes and returns the given node from the list
126
129
@@ -140,8 +143,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None:
140
143
return node
141
144
142
145
143
- # class LRUCache(Generic[T]):
144
- class LRUCache :
146
+ class LRUCache (Generic [T , U ]):
145
147
"""
146
148
LRU Cache to store a given capacity of data. Can be used as a stand-alone object
147
149
or as a function decorator.
@@ -206,17 +208,15 @@ class LRUCache:
206
208
"""
207
209
208
210
# class variable to map the decorator functions to their respective instance
209
- decorator_function_to_instance_map : dict [Callable , LRUCache ] = {}
211
+ decorator_function_to_instance_map : dict [Callable [[ T ], U ], LRUCache [ T , U ] ] = {}
210
212
211
213
def __init__ (self , capacity : int ):
212
- self .list = DoubleLinkedList ()
214
+ self .list : DoubleLinkedList [ T , U ] = DoubleLinkedList ()
213
215
self .capacity = capacity
214
216
self .num_keys = 0
215
217
self .hits = 0
216
218
self .miss = 0
217
- # self.cache: dict[int, int] = {}
218
- # self.cache: dict[int, T] = {}
219
- self .cache : dict [int , DoubleLinkedListNode ] = {}
219
+ self .cache : dict [T , DoubleLinkedListNode [T , U ]] = {}
220
220
221
221
def __repr__ (self ) -> str :
222
222
"""
@@ -229,7 +229,7 @@ def __repr__(self) -> str:
229
229
f"capacity={ self .capacity } , current size={ self .num_keys } )"
230
230
)
231
231
232
- def __contains__ (self , key : int ) -> bool :
232
+ def __contains__ (self , key : T ) -> bool :
233
233
"""
234
234
>>> cache = LRUCache(1)
235
235
@@ -244,15 +244,15 @@ def __contains__(self, key: int) -> bool:
244
244
245
245
return key in self .cache
246
246
247
- def get (self , key : int ) -> int | None :
247
+ def get (self , key : T ) -> U | None :
248
248
"""
249
249
Returns the value for the input key and updates the Double Linked List.
250
250
Returns None if key is not present in cache
251
251
"""
252
252
253
253
if key in self .cache :
254
254
self .hits += 1
255
- value_node = self .cache [key ]
255
+ value_node : DoubleLinkedListNode [ T , U ] = self .cache [key ]
256
256
node = self .list .remove (self .cache [key ])
257
257
assert node == value_node
258
258
@@ -263,7 +263,7 @@ def get(self, key: int) -> int | None:
263
263
self .miss += 1
264
264
return None
265
265
266
- def set (self , key : int , value : int ) -> None :
266
+ def set (self , key : T , value : U ) -> None :
267
267
"""
268
268
Sets the value for the input key and updates the Double Linked List
269
269
"""
@@ -277,7 +277,9 @@ def set(self, key: int, value: int) -> None:
277
277
# explain to type checker via assertions
278
278
assert first_node is not None
279
279
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
280
+ assert (
281
+ self .list .remove (first_node ) is not None
282
+ ) # node guaranteed to be in list assert node.key is not None
281
283
282
284
del self .cache [first_node .key ]
283
285
self .num_keys -= 1
@@ -293,14 +295,15 @@ def set(self, key: int, value: int) -> None:
293
295
self .list .add (node )
294
296
295
297
@staticmethod
296
- def decorator (size : int = 128 ) -> Callable [[Callable [[int ], int ]], Callable [..., int ]]:
298
+ def decorator (size : int = 128 ) -> Callable [[Callable [[T ], U ]], Callable [..., U ]]:
297
299
"""
298
300
Decorator version of LRU Cache
299
301
300
- Decorated function must be function of int -> int
302
+ Decorated function must be function of T -> U
301
303
"""
302
- def cache_decorator_inner (func : Callable [[int ], int ]) -> Callable [..., int ]:
303
- def cache_decorator_wrapper (* args : int ) -> int :
304
+
305
+ def cache_decorator_inner (func : Callable [[T ], U ]) -> Callable [..., U ]:
306
+ def cache_decorator_wrapper (* args : T ) -> U :
304
307
if func not in LRUCache .decorator_function_to_instance_map :
305
308
LRUCache .decorator_function_to_instance_map [func ] = LRUCache (size )
306
309
0 commit comments