Запоминание кэша фляги, не работающее с исходными ресурсами фляги
flask_cache.Cache.memoize
не работает с flask_restful.Resouce
Вот пример кода:
from flask import Flask, request, jsonify
from flask_restful import Resource, Api
from flask_cache import Cache
app = Flask(__name__)
api = Api(app)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
class MyResource(Resource):
JSONIFY = True
PATH = None
ENDPOINT = None
def dispatch_request(self, *args, **kwargs):
kw = dict(**kwargs)
kw.update(request.args.items())
r = super().dispatch_request(*args, **kw)
if self.JSONIFY:
return jsonify(r)
else:
return r
class DebugResouce(MyResource):
PATH = '/debug'
ENDPOINT = 'debug'
@cache.memoize(timeout=30)
def get(self, **kwargs):
print('cache is not used!')
return kwargs
for r in [DebugResouce]:
api.add_resource(r, r.PATH, endpoint=r.ENDPOINT)
print('running!')
app.run()
Обратите внимание, что в get()
Я добавил print, чтобы видеть, когда код на самом деле вызывается и когда используется кэшированное значение.
Я запускаю сервер, затем в браузере, я иду к http://localhost:5000/debug?a=1
и нажмите f5
repeatetely. Я ожидаю, что моя функция get
вызывается один раз, а затем используется кэшированное значение. Но в консоли сервера я вижу свой отпечаток каждый раз, когда нажимаю f5
, Так memoize
не работает. Что я делаю неправильно?
редактировать:
Я переместил свою кэшированную функцию снаружи из Resource
учебный класс
@cache.memoize(timeout=30)
def my_foo(a):
print('cache is not used!')
return dict(kw=a, id=id(a))
class DebugResouce(MyResource):
PATH = '/debug'
ENDPOINT = 'debug'
def get(self, a):
return my_foo(a)
и это сработало. Насколько я вижу, проблема была self
аргумент, который был на самом деле уникальным в каждом вызове. Вопрос все еще, как заставить это работать без извлечения дополнительной функции для каждого метода, который я хочу кэшировать? Текущее решение выглядит как обходной путь.
4 ответа
Спасибо @Rugnar, это решение пригодилось.
решение
Единственный момент, мне пришлось немного его изменить, чтобы я не исключал первый элемент (self), а использовал его, чтобы хранить больше уникальных ключей в ситуации, когда кэшированный метод определен в базовом классе, и у детей они настроены.
метод _extract_self_arg
обновлено.
class ResourceCache(Cache):
""" When the class method is being memoized,
cache key uses the class name from self or cls."""
def _memoize_make_cache_key(self, make_name=None, timeout=None):
def make_cache_key(f, *args, **kwargs):
fname, _ = function_namespace(f)
if callable(make_name):
altfname = make_name(fname)
else:
altfname = fname
updated = altfname + json.dumps(dict(
args=self._extract_self_arg(f, args),
kwargs=kwargs), sort_keys=True)
return b64encode(
md5(updated.encode('utf-8')).digest()
)[:16].decode('utf-8')
return make_cache_key
@staticmethod
def _extract_self_arg(f, args):
argspec_args = inspect.getargspec(f).args
if argspec_args and argspec_args[0] in ('self', 'cls'):
if hasattr(args[0], '__name__'):
return (args[0].__name__,) + args[1:]
return (args[0].__class__.__name__,) + args[1:]
return args
Может быть, это тоже будет кому-то полезно.
Кеш не работает, потому что вы используете метод memoize. В этом случае он будет кешировать результат функции. Декоратор ничего не знает о маршруте (вид, путь).
Чтобы исправить это, вы должны использовать кэшированный метод. @cached
у декоратора есть аргумент key_prefix
со значением по умолчанию = view/request.path
,
Так что просто поменяй @cache.memoize(timeout=30)
в @cache.cached(timeout=30)
Это не работает, потому что memoize учитывает аргументы функции в ключе кеша, и каждый новый запрос становится уникальным kwargs
(уникальный результат id
функция).
Чтобы увидеть, просто измените код
@cache.memoize(timeout=30)
def get(self, **kwargs):
print('cache is not used!')
return id(kwargs)
и каждый новый запрос вы получите другой результат. Так что каждый новый ключ кеша запроса отличается, поэтому вы видите cache is not used!
на выходе консоли.
Найденное решение по подклассам Cache
и перегрузка логики, которая создает ключ кеша для memoize
, Так что все работает отлично.
import json
import inspect
from base64 import b64encode
from hashlib import md5
from flask_cache import Cache, function_namespace
class ResourceCache(Cache):
def _memoize_make_cache_key(self, make_name=None, timeout=None):
def make_cache_key(f, *args, **kwargs):
fname, _ = function_namespace(f)
if callable(make_name):
altfname = make_name(fname)
else:
altfname = fname
updated = altfname + json.dumps(dict(
args=self._extract_self_arg(f, args),
kwargs=kwargs), sort_keys=True)
return b64encode(
md5(updated.encode('utf-8')).digest()
)[:16].decode('utf-8')
return make_cache_key
@staticmethod
def _extract_self_arg(f, args):
argspec_args = inspect.getargspec(f).args
if argspec_args and argspec_args[0] in ('self', 'cls'):
return args[1:]
return args
Другими словами, когда метод класса запоминается, кеш игнорирует первый аргумент self
или же cls
,