@@ -142,6 +142,30 @@ def __ne__(self, other):
142142
143143_CacheInfo = namedtuple ("CacheInfo" , ["hits" , "misses" , "maxsize" , "currsize" ])
144144
145+ class _CacheKey (list ):
146+ 'Make a cache key from optionally typed positional and keyword arguments'
147+
148+ __slots__ = 'hashvalue'
149+
150+ def __init__ (self , args , kwds , typed ,
151+ kwd_mark = (object (),),
152+ sorted = sorted , tuple = tuple , type = type , hash = hash ):
153+ key = args
154+ if kwds :
155+ sorted_items = sorted (kwds .items ())
156+ key += kwd_mark
157+ for item in sorted_items :
158+ key += item
159+ if typed :
160+ key += tuple (type (v ) for v in args )
161+ if kwds :
162+ key += tuple (type (v ) for k , v in sorted_items )
163+ self [:] = key
164+ self .hashvalue = hash (key ) # so we only have to hash just once
165+
166+ def __hash__ (self ):
167+ return self .hashvalue
168+
145169def lru_cache (maxsize = 100 , typed = False ):
146170 """Least-recently-used cache decorator.
147171
@@ -168,8 +192,8 @@ def lru_cache(maxsize=100, typed=False):
168192 # to allow the implementation to change (including a possible C version).
169193
170194 # Constants shared by all lru cache instances:
171- kwd_mark = (object (),) # separate positional and keyword args
172195 sentinel = object () # unique object used to signal cache misses
196+ make_key = _CacheKey # build a key from the function arguments
173197 PREV , NEXT , KEY , RESULT = 0 , 1 , 2 , 3 # names for the link fields
174198
175199 def decorating_function (user_function ):
@@ -182,20 +206,6 @@ def decorating_function(user_function):
182206 root = [] # root of the circular doubly linked list
183207 root [:] = [root , root , None , None ] # initialize by pointing to self
184208
185- def make_key (args , kwds , typed , tuple = tuple , sorted = sorted , type = type ):
186- # build a cache key from positional and keyword args
187- key = args
188- if kwds :
189- sorted_items = tuple (sorted (kwds .items ()))
190- key += kwd_mark
191- key += tuple (k for k , v in sorted_items )
192- key += tuple (v for k , v in sorted_items )
193- if typed :
194- key += tuple (type (v ) for v in args )
195- if kwds :
196- key += tuple (type (v ) for k , v in sorted_items )
197- return key
198-
199209 if maxsize == 0 :
200210
201211 def wrapper (* args , ** kwds ):
@@ -210,7 +220,7 @@ def wrapper(*args, **kwds):
210220 def wrapper (* args , ** kwds ):
211221 # simple caching without ordering or size limit
212222 nonlocal hits , misses , currsize
213- key = make_key (args , kwds , typed ) if kwds or typed else args
223+ key = make_key (args , kwds , typed )
214224 result = cache_get (key , sentinel )
215225 if result is not sentinel :
216226 hits += 1
@@ -226,7 +236,7 @@ def wrapper(*args, **kwds):
226236 def wrapper (* args , ** kwds ):
227237 # size limited caching that tracks accesses by recency
228238 nonlocal root , hits , misses , currsize , full
229- key = make_key (args , kwds , typed ) if kwds or typed else args
239+ key = make_key (args , kwds , typed )
230240 with lock :
231241 link = cache_get (key )
232242 if link is not None :
0 commit comments