【问题标题】:Decorate re-using another decorator's implementation装饰重用另一个装饰器的实现
【发布时间】:2012-12-30 14:35:43
【问题描述】:

我已经实现了一个memoize 装饰器,它允许缓存一个函数。缓存键包括函数参数。类似地,cached 装饰器缓存了一个函数,但忽略了参数。代码如下:

class ApplicationCache (Memcached):

make_key 方法:UltraJSON 快速传递一个字符串,SHA512 散列成清晰的十六进制摘要:

    def make_key (self, *args, **kwargs):

        kwargs.update (dict (enumerate (args)))
        string = ujson.encode (sorted (kwargs.items ()))
        hashed = hashlib.sha512 (string)

        return hashed.hexdigest ()

memoize 装饰器:因为 Python 2.x 糟透了 w.r.t。完全限定的函数名,我只是强制用户提供一个合理的name

    def memoize (self, name, timeout=None):
        assert name

        def decorator (fn):
            @functools.wraps (fn)
            def decorated (*args, **kwargs):

                key = self.make_key (name, *args, **kwargs)
                cached = self.get (key)

                if cached is None:
                    cached = fn (*args, **kwargs)
                    self.set (key, cached, timeout=timeout)

                return cached
            return decorated
        return decorator

cached 装饰器:它几乎是memoize 的逐字复制,唯一的例外是make_key 忽略了参数:

    def cached (self, name, timeout=None):
        assert name

        def decorator (fn):
            @functools.wraps (fn)
            def decorated (*args, **kwargs):

                key = self.make_key (name) ## no args!
                cached = self.get (key)

                if cached is None:
                    cached = fn (*args, **kwargs)
                    self.set (key, cached, timeout=timeout)

                return cached
            return decorated
        return decorator

现在,我对cached 的问题是,它需要重构:它应该使用memoize,并且想法是消除fn 的参数(也许使用functools.partial?),比如:

    def cached (self, name, timeout=None):

        ## Reuse the more general `memoize` to cache a function,
        ## but only based on its name (ignoring the arguments)

我实际上不确定我是否在这里过度使用 DRY 原则,以及是否可以重用,因为 cached 的当前实现在构建密钥时忽略了参数 only (但显然 不是 在调用装饰函数时)。

【问题讨论】:

    标签: python decorator partial code-reuse


    【解决方案1】:

    我会去掉 name 参数并提供 key 函数作为参数:

    def memoize(self, timeout=None, keyfunc=self.make_key):
        ...
        key = keyfunc(function.__name__, *args, **kwargs)
        ...
    

    cache 将变成:

    def cache(self, timeout=None):
        return self.memoize(timeout, keyfunc=lambda f, *args, **kwargs: f.__name__)
    

    【讨论】:

    • 太棒了,与装饰师一起工作一定让我对这个明显的解决方案视而不见。谢谢你。 ;D 要点在这里:gist.github.com/4417820
    • @hsk81:顺便问一下,你为什么使用 JSON 来字符串化参数?最好只做str(),因为这样会更快,并且适用于不可序列化的对象。
    • 谢谢你是对的,将JSON.encode替换为unicode(使用会话IDsid而不是会话);更新的要点可在gist.github.com/4418107 获得。
    猜你喜欢
    • 2011-09-03
    • 2017-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-29
    • 1970-01-01
    • 2012-02-16
    • 1970-01-01
    相关资源
    最近更新 更多