我如何编写 python 缓存装饰器?

How can i write python decorator for caching?

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

  1. @memoize 如何转换为 memoize class 的 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)
  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)