Python Flask缓存memoize不使用Flask restful资源

Python Flask缓存memoize不使用Flask restful资源,python,caching,flask,flask-restful,flask-cache,Python,Caching,Flask,Flask Restful,Flask Cache,flask\u cache.cache.memoize不使用flask\u 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_TY

flask\u cache.cache.memoize
不使用
flask\u 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()
中,我添加了print,以便查看何时实际调用了代码以及何时使用了缓存值

我启动服务器,然后在浏览器中转到
http://localhost:5000/debug?a=1
然后反复按
f5
。我希望调用一次我的函数
get
,然后使用缓存的值。但在服务器控制台中,我每次按
f5
时都会看到打印内容。所以
memoize
不起作用。我做错了什么

编辑:

我将缓存的函数从
资源
类移出

@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和每个新请求都会得到唯一的
kwargs
(唯一的
id

要查看,只需修改代码即可

@cache.memoize(timeout=30)
def get(self, **kwargs):
    print('cache is not used!')
    return id(kwargs)

每一个新的请求你都会得到另一个结果。因此,每个新的请求缓存键都是不同的,这就是为什么不使用
缓存

缓存不工作,因为您使用了方法。在这种情况下,它将缓存函数的结果。装饰者对路线(视图、路径)一无所知

要修复它,您应该使用方法
@cached
decorator具有参数
key\u前缀
,默认值=
view/request.path


所以,只需将
@cache.memoize(timeout=30)
更改为
@cache.cached(timeout=30)
通过子类化
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

换句话说,当类方法被记忆时,cache会忽略第一个参数
self
cls

谢谢@Rugnar,这个决定很有用

唯一的一点是,我必须对它做一点修改,这样我就不会排除第一个元素(self),而是使用它,以便在缓存方法在基类中定义的情况下存储更多的唯一键,并在子类中自定义它们

方法
\u extract\u self\u 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

也许它对某些人也有用。

使用
**kwargs
不是一个好的解决方案,因为其他开发人员不会看到函数的签名。输入数据将像一个黑匣子。@DanilaGanchar我不是说这是一个好的做法,这只是一个OP的代码,我用它来描述缓存“不工作”的原因。我尝试了这个,
id
没有改变,但问题仍然是
@cache.memoize(timeout=30)
def get(self,a):
打印('cache is not used!')
return dict(kw=a,id=id(a))
@Rugnar只需使用
@cache.cached
,正如Danila Ganchar建议并定义了可调用的
key\u前缀
:我不知道是谁为你的问题点击了
downvote
,但我不明白你为什么要点击
downvote
。我想帮忙,我确信我的解决方案是可行的。你能解释一下你为什么这么做吗?很抱歉,我点击了下一票,因为这不是我所期望的,尽管你的解决方案会奏效。我想更改我的投票,但在你编辑答案之前,我不允许更改。我的观点是,我希望我的缓存将函数参数考虑在内,并且只考虑它们。所以我可以用同样的方式装饰资源方法和任何其他函数。因此,当缓存在args上使用
request.path
作为键时,它不适合我。问题是arg
self
会影响缓存密钥的计算,并使其在每个请求中都是唯一的。您是否可以进行回滚
向下投票
?我更新了我的答案。谢谢。祝你发展顺利;)