在無內(nèi)置支持時(shí)實(shí)現(xiàn)的最少使用cache
# coding: utf8
try:
from functools import lru_cache
except ImportError:
from collections import namedtuple
from functools import update_wrapper
from threading import RLock
# 緩存情況說明 這個(gè)是在函數(shù)內(nèi)
# 每個(gè)被lru_cache包裹的函數(shù)都有其自身的緩存
_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])
class _HashedSeq(list):
__slots__ = 'hashvalue'
def __init__(self, tup, hash=hash):
self[:] = tup
self.hashvalue = hash(tup)
def __hash__(self):
return self.hashvalue
def _make_key(args, kwds, typed,
kwd_mark = (object(),),
fasttypes = {int, str, frozenset, type(None)},
sorted=sorted, tuple=tuple, type=type, len=len):
key = args
if kwds:
sorted_items = sorted(kwds.items())
key += kwd_mark
for item in sorted_items:
key += item
if typed:
key += tuple(type(v) for v in args)
if kwds:
key += tuple(type(v) for k, v in sorted_items)
elif len(key) == 1 and type(key[0]) in fasttypes:
return key[0]
return _HashedSeq(key)
# 最少使用的 使用次數(shù)決定是否剔除
def lru_cache(maxsize=100, typed=False):
def decorating_function(user_function):
"""
函數(shù)裝飾器 user_functions即為被包裹的函數(shù)
:param user_function:
:return:
"""
stats = [0, 0] # 統(tǒng)計(jì)
HITS, MISSES = 0, 1 # HITS -> stats[0]
# MISSES -> stats[1]
# 緩存就是用dict,key則為_make_key計(jì)算出來的,獲取方法則為dict的get方法
cache = dict()
make_key = _make_key
cache_get = cache.get
_len = len # 把全局函數(shù)len局部化 提升性能
# 用于指定長度的cache的
# 使用長度為4的list作為節(jié)點(diǎn) 包含兩個(gè)指針和key value
lock = RLock()
root = []
root[:] = [root, root, None, None] # 分別指向 前 后 和 key value
nonlocal_root = [root]
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3
if maxsize == 0:
def wrapper(*args, **kwds):
"""
如果cache大小為0 就是沒緩存
:param args:
:param kwds:
:return:
"""
result = user_function(*args, **kwds) # 每次都是調(diào)用函數(shù)計(jì)算
stats[MISSES] += 1 # 每次都是未命中的情況
return result
elif maxsize is None:
def wrapper(*args, **kwds):
"""
無限大小的cache
:param args:
:param kwds:
:return:
"""
key = make_key(args, kwds, typed) # 計(jì)算出這些參數(shù)對應(yīng)的key
result = cache_get(key, root) # root作為哨兵
if result is not root:
# 找到了
stats[HITS] += 1 # 命中
return result
# 沒找到
result = user_function(*args, **kwds)
cache[key] = result
stats[MISSES] += 1
return result
else:
def wrapper(*args, **kwds):
"""
有大小限制的cache
:param args:
:param kwds:
:return:
"""
key = make_key(args, kwds, typed) if kwds or typed else args
with lock:
link = cache_get(key)
# 找到了
if link is not None:
root, = nonlocal_root
link_prev, link_next, key, result = link # 解出來 4個(gè)值
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
# 把這個(gè)值從鏈中解下來
last = root[PREV]
last[NEXT] = root[PREV] = link
link[PREV] = last
link[NEXT] = root
# 連接在鏈最后
stats[HITS] += 1
return result
result = user_function(*args, **kwds)
with lock:
root, = nonlocal_root
if key in cache:
# 這個(gè)可能是在剛剛釋放鎖后有人計(jì)算出來了,所以這邊就返回就行了,不用再進(jìn)行緩存相關(guān)的處理了
pass
elif _len(cache) >= maxsize:
# 緩存區(qū)滿的情況
oldroot = root
oldroot[KEY] = key
oldroot[RESULT] = result
root = nonlocal_root[0] = oldroot[NEXT]
oldkey = root[KEY]
oldvalue = root[RESULT]
root[KEY] = root[RESULT] = None
del cache[oldkey]
cache[key] = oldroot
else:
last = root[PREV]
link = [last, root, key, result]
last[NEXT] = root[PREV] = cache[key] = link
stats[MISSES] += 1
return result
def cache_info():
with lock:
return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache))
def cache_clear():
"""
清空緩存
:return:
"""
with lock:
cache.clear()
root = nonlocal_root[0]
root[:] = [root, root, None, None]
stats[:] = [0, 0]
wrapper.__wrapped__ = user_function
wrapper.cache_info = cache_info
wrapper.cache_clear = cache_clear
return update_wrapper(wrapper, user_function)
return decorating_function