2017-06-15 146 views
1

我有一个记忆化装饰,看起来像这样:Memoizing装饰保持存储的值

def memoize(obj): 
    from functools import wraps 
    cache = {} 

    @wraps(obj) 
    def memoizer(*args, **kwargs): 
     if args not in cache: 
      cache[args] = obj(*args, **kwargs) 
     return cache[args] 

    return memoizer 

但是,我不知道这个功能的正常使用,因为它好像对我来说,重新创建cache作为一个空的字典每调用装饰函数的时间。当我用一个简单的斐波那契函数测试它时,它似乎确实正确记忆。那么cache每次都不会重新创建?

的包括该行python wiki has a version

cache = obj.cache = {} 

所以我不知道这是什么一样。我猜python函数是对象,所以它正在创建一个与该函数关联的新属性,并且每次调用该函数时都会公开可用/可用。

在任一版本中,如果我重复调用函数,如递归定义或仅通过重复调用,缓存如何处理?它是否与函数关联,它是否成为“全局”变量或其他东西?

+0

“每次” - 每个*什么*?每次调用memoize时,它都会创建一个新的'cache',但每次调用memoized函数时都不会调用memoize。 – user2357112

回答

2

1)每次调用memoize()时都会创建一个新的cache。但每个装饰函数只调用一次memoize()。因此每个装饰功能都有自己的cache

2)关于cache = obj.cache = {}

函数也是对象在Python。正如您已经假设的那样,obj.cache = {}将在包装函数上创建一个新属性。包装函数上的本地缓存对象和缓存属性将指向同一个字典。

这不是人,cache是一个全局变量 - 它在memoize函数中是本地的,它是函数的一个属性。

如果您有装饰函数f您可以在全局范围内访问f.cache,如果这就是您对全局变量的含义。

如果您重复调用装饰函数,它将始终访问特定于装饰函数的相同cache


解决内部函数不“bemeing”memoized“。

def outer(): 
    # do not add the decorator here! 
    def inner(): 
     return 5 
    if(not hasattr(outer, "inner")): 
     # the "@" decorator is only syntactical sugar, 
     # we can simply call the decorator function to wrap another function 
     outer.inner = memoize(inner) 
    outer.inner() 
    return outer.inner() 

outer() 
outer() 
+0

如果你提到*闭包*并且'memoizer' *关闭了它的环境,包括'cache'和'obj',这个答案可能会更好。 (另外,“它自己”中没有撇号。) –

+0

简要跟进:我测试了在另一个函数内定义的装饰函数,所以每次缓存被重置。这是因为函数定义在超出范围后不见了? – qwr

+0

@qwr正好。如果向memoize函数添加'print(“memoize called”,obj)'',您将看到如果再次调用外部函数,则内部函数会发生变化。但如果在外部函数内多次调用内部函数,它仍然在相同的范围内。 –