Max*_*Max 7 python lru python-3.x python-decorators
这是一个简化的函数,我试图为其添加一个lru_cachefor -
from functools import lru_cache, wraps
@lru_cache(maxsize=1000)
def validate_token(token):
if token % 3:
return None
return True
for x in range(1000):
validate_token(x)
print(validate_token.cache_info())
Run Code Online (Sandbox Code Playgroud)
输出 -
CacheInfo(hits=0, misses=1000, maxsize=1000, currsize=1000)
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,它也会缓存args返回returned值None。在上面的示例中,我希望cache_size为 334,我们返回非 None 值。就我而言,我的函数有很大的编号。如果之前的值为 ,则ofargs可能会返回不同的值None。所以我想避免缓存None值。
我想避免重新发明轮子并lru_cache从头开始实施。有什么好的方法可以做到这一点吗?
以下是我的一些尝试 -
1.尝试实现自己的缓存(这里不是lru)-
from functools import wraps
# global cache object
MY_CACHE = {}
def get_func_hash(func):
# generates unique key for a function. TODO: fix what if function gets redefined?
return func.__module__ + '|' + func.__name__
def my_lru_cache(func):
name = get_func_hash(func)
if not name in MY_CACHE:
MY_CACHE[name] = {}
@wraps(func)
def function_wrapper(*args, **kwargs):
if tuple(args) in MY_CACHE[name]:
return MY_CACHE[name][tuple(args)]
value = func(*args, **kwargs)
if value is not None:
MY_CACHE[name][tuple(args)] = value
return value
return function_wrapper
@my_lru_cache
def validate_token(token):
if token % 3:
return None
return True
for x in range(1000):
validate_token(x)
print(get_func_hash(validate_token))
print(len(MY_CACHE[get_func_hash(validate_token)]))
Run Code Online (Sandbox Code Playgroud)
输出 -
__main__|validate_token
334
Run Code Online (Sandbox Code Playgroud)
2.我意识到,当在包装函数中引发lru_cachean 时,不会进行缓存-exception
from functools import wraps, lru_cache
def my_lru_cache(func):
@wraps(func)
@lru_cache(maxsize=1000)
def function_wrapper(*args, **kwargs):
value = func(*args, **kwargs)
if value is None:
# TODO: change this to a custom exception
raise KeyError
return value
return function_wrapper
def handle_exception(func):
@wraps(func)
def function_wrapper(*args, **kwargs):
try:
value = func(*args, **kwargs)
return value
except KeyError:
return None
return function_wrapper
@handle_exception
@my_lru_cache
def validate_token(token):
if token % 3:
return None
return True
for x in range(1000):
validate_token(x)
print(validate_token.__wrapped__.cache_info())
Run Code Online (Sandbox Code Playgroud)
输出 -
CacheInfo(hits=0, misses=334, maxsize=1000, currsize=334)
Run Code Online (Sandbox Code Playgroud)
上面正确地仅缓存了334值,但需要将函数包装两次并cache_info以奇怪的方式访问func.__wrapped__.cache_info()。
当(或特定)值以Pythonic方式None使用内置装饰器返回时,如何更好地实现不缓存的行为?lru_cache
小智 6
使用异常来防止缓存:
from functools import lru_cache
@lru_cache(maxsize=None)
def fname(x):
print('worked')
raise Exception('')
return 1
for _ in range(10):
try:
fname(1)
except Exception as e:
pass
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,“worked”将被打印 10 次。
您缺少此处标记的两行:
def handle_exception(func):
@wraps(func)
def function_wrapper(*args, **kwargs):
try:
value = func(*args, **kwargs)
return value
except KeyError:
return None
function_wrapper.cache_info = func.cache_info # Add this
function_wrapper.cache_clear = func.cache_clear # Add this
return function_wrapper
Run Code Online (Sandbox Code Playgroud)
您可以在一个函数中执行这两个包装器:
def my_lru_cache(maxsize=128, typed=False):
class CustomException(Exception):
pass
def decorator(func):
@lru_cache(maxsize=maxsize, typed=typed)
def raise_exception_wrapper(*args, **kwargs):
value = func(*args, **kwargs)
if value is None:
raise CustomException
return value
@wraps(func)
def handle_exception_wrapper(*args, **kwargs):
try:
return raise_exception_wrapper(*args, **kwargs)
except CustomException:
return None
handle_exception_wrapper.cache_info = raise_exception_wrapper.cache_info
handle_exception_wrapper.cache_clear = raise_exception_wrapper.cache_clear
return handle_exception_wrapper
if callable(maxsize):
user_function, maxsize = maxsize, 128
return decorator(user_function)
return decorator
Run Code Online (Sandbox Code Playgroud)