繁体   English   中英

记住Python中的递归函数,但仅在顶级调用期间

[英]Memoization of recursive functions in Python, but only for the duration of the top-level call

我正在编写一个自上而下的解析器,该解析器由一个顶层函数组成,该顶层函数使用较低层的函数启动递归解析文本。 请注意,下层函数从不调用顶层函数,但下层函数是相互递归的。

我注意到解析器的运行速度有些慢,我怀疑这是由递归的指数增长引起的,因为解析器可能会反复尝试以相同的偏移量解析同一文本上的相同类型的对象,从而浪费了精力。

出于这个原因,我想记住较低级别的函数调用,但是在顶层函数返回之后,我想清除备忘录缓存以释放内存。

这意味着,如果用户使用相同的参数多次调用顶层函数,则该程序实际上应该再次经历整个解析过程。

我的动机是,同一文本不太可能在顶级被多次解析,因此内存开销不值得(每次解析都会生成相当大的缓存)。

一种可能的解决方案是重写所有较低层的函数,以采用这样的附加缓存参数:

def low_level_parse(text, start, cache):
    if (text, start) not in cache:
        # Do something to compute a result
        # ...
        cache[(text, start)] = result

    return cache[(text, start)]

并重写所有对低级函数的调用,以传递下高速缓存参数(该参数最初在顶级函数中设置为{} )。

不幸的是,有许多低级解析函数,并且每个函数也可能多次调用其他低级解析函数。 重构代码以实现这种方式的缓存将非常繁琐且容易出错。

另一个解决方案是使用装饰器,我认为这在可维护性方面是最好的,但是我不知道如何实现备忘录装饰器,因为它的缓存仅存在于顶层函数范围内。

我还考虑过将缓存定义为模块中的全局变量,并在从顶级函数返回后将其明确清除。 这将使我无需修改低级函数以显式采用cache参数,然后可以使用利用全局缓存的备忘录装饰器。 但是我不确定如果在多线程环境中使用全局缓存,将不是一个好主意。

我发现此链接指向带有参数的装饰器 ,我认为这里需要的是:

class LowLevelProxy:

    def __init__(self, cache):
        self.cache = cache

    def __call__(self, f):
        def wrapped_f(*args, **kwargs):
            key = (f,args)    # <== had to remove kwargs as dicts cannot be keys
            if key not in self.cache:
                result = f(*args, **kwargs)
                self.cache[key] = result

            return self.cache[key]
        return wrapped_f

注意,每个包装的函数在缓存中都有自己的部分。

您可以像这样包装每个低层函数:

@LowLevelProxy(cache)
def low_level(param_1, param_2):
    # do stuff

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM