1212 'total_ordering' , 'cmp_to_key' , 'lru_cache' , 'reduce' , 'partial' ]
1313
1414from _functools import partial , reduce
15- from collections import OrderedDict , namedtuple
15+ from collections import namedtuple
1616try :
1717 from _thread import allocate_lock as Lock
1818except :
@@ -147,17 +147,20 @@ def lru_cache(maxsize=100, typed=False):
147147 # to allow the implementation to change (including a possible C version).
148148
149149 def decorating_function (user_function ,
150- * , tuple = tuple , sorted = sorted , map = map , len = len , type = type , KeyError = KeyError ):
150+ * , tuple = tuple , sorted = sorted , map = map , len = len , type = type ):
151151
152+ cache = dict ()
152153 hits = misses = 0
154+ cache_get = cache .get # bound method for fast lookup
153155 kwd_mark = (object (),) # separates positional and keyword args
154- lock = Lock () # needed because OrderedDict isn't threadsafe
156+ lock = Lock () # needed because linkedlist isn't threadsafe
157+ root = [] # root of circular doubly linked list
158+ root [:] = [root , root , None , None ] # initialize by pointing to self
155159
156160 if maxsize is None :
157- cache = dict () # simple cache without ordering or size limit
158-
159161 @wraps (user_function )
160162 def wrapper (* args , ** kwds ):
163+ # simple caching without ordering or size limit
161164 nonlocal hits , misses
162165 key = args
163166 if kwds :
@@ -167,23 +170,18 @@ def wrapper(*args, **kwds):
167170 key += tuple (map (type , args ))
168171 if kwds :
169172 key += tuple (type (v ) for k , v in sorted_items )
170- try :
171- result = cache [ key ]
173+ result = cache_get ( key )
174+ if result is not None :
172175 hits += 1
173176 return result
174- except KeyError :
175- pass
176177 result = user_function (* args , ** kwds )
177178 cache [key ] = result
178179 misses += 1
179180 return result
180181 else :
181- cache = OrderedDict () # ordered least recent to most recent
182- cache_popitem = cache .popitem
183- cache_renew = cache .move_to_end
184-
185182 @wraps (user_function )
186183 def wrapper (* args , ** kwds ):
184+ # size limited caching that tracks accesses by recency
187185 nonlocal hits , misses
188186 key = args
189187 if kwds :
@@ -193,20 +191,33 @@ def wrapper(*args, **kwds):
193191 key += tuple (map (type , args ))
194192 if kwds :
195193 key += tuple (type (v ) for k , v in sorted_items )
194+ PREV , NEXT = 0 , 1 # names of link fields
196195 with lock :
197- try :
198- result = cache [key ]
199- cache_renew (key ) # record recent use of this key
196+ link = cache_get (key )
197+ if link is not None :
198+ link = cache [key ]
199+ # record recent use of the key by moving it to the front of the list
200+ link_prev , link_next , key , result = link
201+ link_prev [NEXT ] = link_next
202+ link_next [PREV ] = link_prev
203+ last = root [PREV ]
204+ last [NEXT ] = root [PREV ] = link
205+ link [PREV ] = last
206+ link [NEXT ] = root
200207 hits += 1
201208 return result
202- except KeyError :
203- pass
204209 result = user_function (* args , ** kwds )
205210 with lock :
206- cache [key ] = result # record recent use of this key
207- misses += 1
211+ last = root [PREV ]
212+ link = [last , root , key , result ]
213+ cache [key ] = last [NEXT ] = root [PREV ] = link
208214 if len (cache ) > maxsize :
209- cache_popitem (0 ) # purge least recently used cache entry
215+ # purge least recently used cache entry
216+ old_prev , old_next , old_key , old_result = root [NEXT ]
217+ root [NEXT ] = old_next
218+ old_next [PREV ] = root
219+ del cache [old_key ]
220+ misses += 1
210221 return result
211222
212223 def cache_info ():
0 commit comments