4

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()我添加了 print 以便我可以看到实际调用代码的时间以及使用缓存值的时间。

我启动服务器然后在浏览器中我去http://localhost:5000/debug?a=1 并重复按f5。我希望我的函数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 DebugResource(MyResource):
    PATH = '/debug'
    ENDPOINT = 'debug'

    def get(self, a):
        return my_foo(a)

那行得通。据我所知,问题self在于每次通话中实际上都是独一无二的参数。问题仍然是,如何在不为我要缓存的每个方法提取附加功能的情况下使其工作?当前的解决方案看起来像是一种解决方法。

4

4 回答 4

4

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

要修复它,您应该使用缓存方法。@cached装饰器具有key_prefix默认值 =的参数view/request.path

所以,只需更改@cache.memoize(timeout=30)@cache.cached(timeout=30)

于 2017-03-11T18:35:04.407 回答
2

感谢@Rugnar,这个决定派上了用场。

解决方案

The only point, I had to change it a bit, so that I would not exclude the first element (self), but use it, in order to store more unique keys in a situation where the cached method is defined in the base class, and in the children they are customized.

Method _extract_self_arg updated.

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

Maybe it will also be useful to someone.

于 2018-06-19T09:06:11.723 回答
1

它不起作用,因为 memoize在缓存键中考虑了函数的参数,并且每个新请求都是唯一的(函数kwargs的唯一结果)。id

要查看,只需修改代码

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

每一个新的请求你都会得到另一个结果。所以每个新的请求缓存键都是不同的,这就是你cache is not used!在控制台输出中看到的原因。

于 2017-03-11T18:34:14.267 回答
0

通过子类化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

换句话说,当类方法被记忆时,缓存忽略第一个参数selfcls

于 2017-03-15T11:44:26.630 回答