模拟一个';局部静态';python中的变量

模拟一个';局部静态';python中的变量,python,Python,考虑以下代码: def CalcSomething(a): if CalcSomething._cache.has_key(a): return CalcSomething._cache[a] CalcSomething._cache[a] = ReallyCalc(a) return CalcSomething._cache[a] CalcSomething._cache = { } 这是我能想到的在python中模拟“局部静态”变量的最简单方法。 困

考虑以下代码:

def CalcSomething(a):
    if CalcSomething._cache.has_key(a):
      return CalcSomething._cache[a]
    CalcSomething._cache[a] = ReallyCalc(a)
    return CalcSomething._cache[a] 

CalcSomething._cache = { }
这是我能想到的在python中模拟“局部静态”变量的最简单方法。
困扰我的是CalcSomething.\u cache在函数的定义之外被提及,但另一种选择是这样的:

if not hasattr(CalcSomething, "_cache"):  
    setattr(CalcSomething, "_cache", { } )  
在函数的定义内部,这真的很麻烦

还有更优雅的方式吗

[编辑]
要澄清的是,这个问题与本地函数缓存无关,正如上面的示例所示。下面是另一个简短的示例,其中“静态本地”可能很方便:

def ParseString(s):
    return ParseString._parser.parse(s)  
# Create a Parser object once, which will be used for all parsings.
# Assuming a Parser object is heave on resources, for the sake of this example.
ParseString._parser = Parser() 

把它变成一个装饰师

def static_var(var_name, initial_value):
    def _set_var(obj):
        setattr(obj, var_name, initial_value)
        return obj
    return _set_var

@static_var("_cache", {})
def CalcSomething(a):
    ...

考虑编写维护缓存的decorator,这样您的函数就不会被缓存代码污染:

def cacheResults(aFunc):
    '''This decorator funcion binds a map between the tuple of arguments 
       and results computed by aFunc for those arguments'''
    def cachedFunc(*args):
        if not hasattr(aFunc, '_cache'):
            aFunc._cache = {}
        if args in aFunc._cache:
            return aFunc._cache[args]
        newVal = aFunc(*args)
        aFunc._cache[args] = newVal
        return newVal
    return cachedFunc

@cacheResults
def ReallyCalc(a):
    '''This function does only actual computation'''
    return pow(a, 42)

也许一开始它看起来不太好,但您可以在任何不需要关键字参数的地方使用
cacheResults()
。可以创建同样适用于关键字参数的类似decorator,但这次似乎没有必要这样做。

将其转换为可调用对象(因为它实际上就是这样的。)


现在,您可以像使用函数一样使用
calcSomething
。但它仍然保持整洁和独立

一个选项是滥用默认参数。即:

def CalcSomething(a, _cache={}):
    if _cache.has_key(a):
这样做的好处是,您不需要限定名称,并且将获得对变量的快速本地访问,而不是进行两次缓慢的dict查找。但是,它仍然存在一个问题,即它是在函数之外被提到的(事实上,它更糟糕,因为它现在在函数签名中。)

为了防止出现这种情况,更好的解决方案是将函数包装在包含静态的闭包中:

@apply
def CalcSomething():
    cache = {}  # statics go here

    def CalcSomething(a):
        if cache.has_key(a):
            return cache[a]
        cache[a] = ReallyCalc(a)
        return cache[a]
    return CalcSomething

洛特提出的解决方案也是我提出的解决方案

也有一些有用的“记忆化”装饰器,如:

考虑到所有这些,我为您最初尝试使用函数和“静态本地”提供了一个替代方案,它是独立的:

def calc_something(a):

    try:
        return calc_something._cache[a]
    except AttributeError: # _cache is not there
        calc_something._cache= {}
    except KeyError: # the result is not there
        pass

    # compute result here

    calc_something._cache[a]= result
    return result


你真的认为这是一种更优雅的方式吗?不,说真的;-)我不认为静态变量是Python中使用的优雅模式,但装饰器至少封装了属性设置的技术细节。+ 1用于Python属性的冷酷使用。保罗-牡蛎:不知道为什么你不认为这个优雅。如果您想要一种非黑客的方式来实现它,只需使用一个带有实例变量的计算器类。你问的是本地静态的,这是天生的丑陋。嗯,作为实用主义者而不是纯粹主义者(加上写C++代码从1991开始……)我不认为本地静态是丑陋的东西。你需要在SytSyVar结尾写代码>返回Obj/<代码>。没有必要弄乱名称(添加下划线),因为它只与static_var.BTW相关,您可能希望用不同的方式表达您的问题。您不是在寻找局部静态变量,而是在寻找一种将记忆引入函数的方法。您的第二个示例比第一个示例更糟糕。您可以返回datetime.datetime.strptime(dts,“%m-%d-%Y%H:%m:%S”),因为strptime是创建新datetime对象的类方法。实际上不需要创建datetime对象。第二个示例是正确的。虽然这不是我问题的目的(见澄清),但这是一个实现本地缓存的漂亮方案。谢谢。如果没有hasattr,你可以摆脱
。。。通过使
\u缓存
cacheResults
的局部变量来进行条件设置。+1-另一种隐藏缓存实现的解决方案。我没有想到这种方法,但它看起来比我的简单装饰器更强大。对于那些想使用自定义可调用函数作为方法的人,你必须实现正确的方法(谷歌的“python描述符协议”了解更多)。这种使用专门类加上可调用的组合实际上为不同的情况启用了一种“函数工厂”,由ctor参数控制。+1对于调用,我不知道您可以使用python对象+1:functional+state->functor(即定义了调用的类)。不幸的是,apply已从python 3.0中删除。我也发现了一些类似的例子,它们简短、简单、有用。我认为这不是apply的好用法。更明显的是只调用函数。我想这不会起作用。第一次通过将运行除AttributeError之外的
块,将缓存初始化为空dict,然后在
calc\u something中的keyerror失败。\u cache[a]=result
@smido我愿意打赌
dict.\uuu setitem\uuuuu
抛出
内存错误的几率要比抛出
keyerror
好得多
def calc_something(a):

    try:
        return calc_something._cache[a]
    except AttributeError: # _cache is not there
        calc_something._cache= {}
    except KeyError: # the result is not there
        pass

    # compute result here

    calc_something._cache[a]= result
    return result