Flask 缓存 memoize 不适用于 Flask restful 资源
Flask cache memoize not working with flask restful resources
flask_cache.Cache.memoize
不适用于 flask_restful.Resource
示例代码如下:
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 DebugResource(MyResource):
PATH = '/debug'
ENDPOINT = 'debug'
@cache.memoize(timeout=30)
def get(self, **kwargs):
print('cache is not used!')
return kwargs
for r in [DebugResource]:
api.add_resource(r, r.PATH, endpoint=r.ENDPOINT)
print('running!')
app.run()
请注意,在 get()
中,我添加了打印,这样我就可以看到实际调用代码的时间以及使用缓存值的时间。
我启动服务器,然后在浏览器中转到 http://localhost:5000/debug?a=1
并重复按 f5
。我希望我的函数 get
被调用一次,然后使用缓存的值。但是在服务器控制台中,我每次按 f5
时都会看到我的打印件。所以 memoize
不起作用。我做错了什么?
编辑:
我将我的缓存函数从 Resource
class
移到了外面
@cache.memoize(timeout=30)
def my_foo(a):
print('cache is not used!')
return dict(kw=a, id=id(a))
class DebugResource(MyResource):
PATH = '/debug'
ENDPOINT = 'debug'
def get(self, a):
return my_foo(a)
这奏效了。据我所知,问题是 self
参数在每次调用中实际上都是唯一的。
问题仍然是,如何在不为我要缓存的每个方法提取附加功能的情况下使其工作?当前的解决方案看起来像是一种解决方法。
它不起作用,因为 memoize takes function's arguments into account in the cache key and every new request gets unique kwargs
(unique result of id
function)。
想看,直接修改代码
@cache.memoize(timeout=30)
def get(self, **kwargs):
print('cache is not used!')
return id(kwargs)
并且每个新请求您都会得到另一个结果。所以每个新请求 缓存键都是不同的 ,这就是为什么你在控制台输出上看到 cache is not used!
。
缓存不起作用,因为您使用了memoize方法。在这种情况下,它将缓存函数的结果。 Decorator 对 route(view, path) 一无所知。
要修复它,您应该使用 cached 方法。 @cached
装饰器有参数 key_prefix
,默认值 = view/request.path
。
所以,只需将 @cache.memoize(timeout=30)
更改为 @cache.cached(timeout=30)
通过 subclassing 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
换句话说,当class方法被记忆时,缓存忽略第一个参数self
或cls
。
感谢@Rugnar,这个决定派上用场了。
唯一的一点,我不得不稍微改变一下,这样我就不会排除第一个元素(self),而是使用它,以便在定义缓存方法的情况下存储更多唯一键在基础 class 中,在子项中它们是自定义的。
方法_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
也许它对某人也有用。
flask_cache.Cache.memoize
不适用于 flask_restful.Resource
示例代码如下:
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 DebugResource(MyResource):
PATH = '/debug'
ENDPOINT = 'debug'
@cache.memoize(timeout=30)
def get(self, **kwargs):
print('cache is not used!')
return kwargs
for r in [DebugResource]:
api.add_resource(r, r.PATH, endpoint=r.ENDPOINT)
print('running!')
app.run()
请注意,在 get()
中,我添加了打印,这样我就可以看到实际调用代码的时间以及使用缓存值的时间。
我启动服务器,然后在浏览器中转到 http://localhost:5000/debug?a=1
并重复按 f5
。我希望我的函数 get
被调用一次,然后使用缓存的值。但是在服务器控制台中,我每次按 f5
时都会看到我的打印件。所以 memoize
不起作用。我做错了什么?
编辑:
我将我的缓存函数从 Resource
class
@cache.memoize(timeout=30)
def my_foo(a):
print('cache is not used!')
return dict(kw=a, id=id(a))
class DebugResource(MyResource):
PATH = '/debug'
ENDPOINT = 'debug'
def get(self, a):
return my_foo(a)
这奏效了。据我所知,问题是 self
参数在每次调用中实际上都是唯一的。
问题仍然是,如何在不为我要缓存的每个方法提取附加功能的情况下使其工作?当前的解决方案看起来像是一种解决方法。
它不起作用,因为 memoize takes function's arguments into account in the cache key and every new request gets unique kwargs
(unique result of id
function)。
想看,直接修改代码
@cache.memoize(timeout=30)
def get(self, **kwargs):
print('cache is not used!')
return id(kwargs)
并且每个新请求您都会得到另一个结果。所以每个新请求 缓存键都是不同的 ,这就是为什么你在控制台输出上看到 cache is not used!
。
缓存不起作用,因为您使用了memoize方法。在这种情况下,它将缓存函数的结果。 Decorator 对 route(view, path) 一无所知。
要修复它,您应该使用 cached 方法。 @cached
装饰器有参数 key_prefix
,默认值 = view/request.path
。
所以,只需将 @cache.memoize(timeout=30)
更改为 @cache.cached(timeout=30)
通过 subclassing 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
换句话说,当class方法被记忆时,缓存忽略第一个参数self
或cls
。
感谢@Rugnar,这个决定派上用场了。
唯一的一点,我不得不稍微改变一下,这样我就不会排除第一个元素(self),而是使用它,以便在定义缓存方法的情况下存储更多唯一键在基础 class 中,在子项中它们是自定义的。
方法_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
也许它对某人也有用。