【问题标题】:How can i write python decorator for caching?我如何编写用于缓存的 python 装饰器?
【发布时间】:2015-03-24 14:22:53
【问题描述】:

我正在尝试为 memoize 编写 python 装饰器。 我有几个问题。

  1. @memoize 如何转换为 memoize 类的 call 函数?
  2. 为什么 init 需要一个参数。
  3. 缓存存储在哪里?它是与每个函数相关联还是全局变量?即如果我使用@memoize 会有两个缓存对象 多种功能。

..

class memoize:
    def __init__(self):
        self.cache = {}

    def __call__(self, function):
        def wrapper(*args, **kwargs):
            key = str(function.__name__) + str(args) + str(kwargs)
            if key in cache:
                return cache[key]
            else:
                value = function(*args, **kwargs)
                cache[key] = value
                return value
        return wrapper

@memoize
def fib(n):
    if n in (0, 1):
        return 1
    else:
        return fib(n-1) + fib(n-2)

for i in range(0, 10):
    print(fib(i))

我收到编译错误。

Traceback (most recent call last):
  File "memoize.py", line 17, in <module>
    @memoize
TypeError: __init__() takes exactly 1 argument (2 given)

【问题讨论】:

  • 你真的要从头开始写这个吗?
  • 是的。我正在学习 python 装饰器
  • 好的,仔细检查一下,因为有一个 lru_cache 装饰器。

标签: python caching decorator python-decorators


【解决方案1】:
  1. 您应该记住@decorator 只是func = decorator(func) 的语法糖。所以这里有一个区别:

(1)

@decorator 
def func():
     ...

相同
func = decorator(func)  # Just call of __init__
func(...)               # Call of decorator.__call__

但是(2)

@decorator(some_param)
def func():
     ...

类似于

# Call of __init__ plus call of __call__
func = decorator(some_param)(func)  
# Call of closure returned by decorator.__call__
func(...)   
  1. 您已经实现了接受 (2) 语法的参数的装饰器,但在示例 (1) 中使用它们时没有提供它们。这就是__init__ 抱怨的原因,它接收func 作为第二个参数。

  2. 您应该在wrapper 闭包中写入self.cache,因此wrapper 将引用相应的decorator 对象。只写cache 会导致全局变量搜索,因此会失败。

UPD:我将您的代码更改为方法 (1):

class memoize:
    def __init__(self, function):
        self.cache = {}
        self.function = function

    def __call__(self, *args, **kwargs):        
        key = str(args) + str(kwargs)
        if key in self.cache:
            return self.cache[key]

        value = self.function(*args, **kwargs)
        self.cache[key] = value
        return value

@memoize
def fib(n):
    if n in (0, 1):
        return 1
    else:
        return fib(n-1) + fib(n-2)

for i in range(0, 10):
    print(fib(i))

print(fib.cache)

【讨论】:

  • 如果你看到这个例子,wiki.python.org/moin/PythonDecoratorLibrary#Memoizecache 是 memoize 类的成员变量。没有包装器
  • 我已将您的方法更改为 (1) 并发布了代码,但我建议您仔细阅读我的解释。
  • 好的。现在应该如何添加一个ttl?我想使用@memoize(ttl=10)。我尝试在类中的 init 函数中添加一个参数,但又出现错误
  • 嘿嘿,我早就料到你会问这个。比您需要将代码从方法(1)更改为(2)。使用您的原始代码(但更改 cache -> self.cache)。请参阅我对第二个问题的回答。
  • 这是一个非常有用的 Python 装饰器,适用于许多应用程序。感谢分享!
猜你喜欢
  • 2015-07-29
  • 1970-01-01
  • 2012-10-28
  • 1970-01-01
  • 2015-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-29
相关资源
最近更新 更多