2

我使用函数对象作为字典键。我这样做是因为我需要缓存这些函数的结果。这大致是我正在使用的代码:

# module cache.py:
calculation_cache = {}
def cached(func):
  # func takes input as argument
  def new_function(input):
    try:
      result = calculation_cache[(func, input)]
    except KeyError:
      result = func(input)
      calculation_cache[(func, input)] = result
    return result
  return new_function

# module stuff.py
@cached
def func(s):
  # do something time-consuming to s
  # ...
  return result

我可以使用func.__module__ + func.__name__而不是func,但如果func工作正常,我宁愿使用它,因为我担心可能的名称冲突(例如,对于lambda函数或嵌套函数或被另一个具有相同名称的函数替换的函数等)

这似乎工作正常,但我怀疑这可能会在一些难以测试的情况下导致问题。

例如,我担心某个函数会以某种方式被删除,而另一个函数会重用其内存空间。在这种情况下,我的缓存将无效,但它不会知道。这是一个有效的担忧吗?如果是这样,有什么办法可以解决吗?

可以删除函数吗?重新加载模块是否会将函数移动到新地址(从而更改其哈希值,并为新函数释放旧内存地址)?有人(出于某种奇怪的原因)可以简单地删除模块中定义的函数(再次使内存可用于新函数)吗?

如果仅使用 明确定义的函数执行此操作是安全的def,那么我可以禁止使用cachedexcept 作为装饰器(我不知道如何强制执行它,但我可以在文档字符串中记录它cached)。

4

2 回答 2

3

我不确定我能否解决您上述所有问题,但我可以解决其中的一个问题——

我看不出一个函数不能被垃圾收集的任何原因。但是,由于您的函数是字典中的键,只要该字典存在,您的函数的引用计数就永远不会达到零,并且不会受到垃圾回收的影响。

但是,我不知道重新加载模块,这似乎是一个极端情况,您实际上不需要担心。模块并不是真的要重新加载......在某些情况下您可以这样做的事实主要是为了在交互式终端中进行调试,而不是用于任何实际代码......(据我所知反正 ...)

于 2012-09-07T04:30:47.537 回答
1

您的代码应该可以工作。就像其他人说的那样,由于函数对象仍然被字典引用,它不会被垃圾收集。该功能可以被删除或模块重新加载。这意味着func标签将引用新版本的函数对象(或不引用)。但是即使不再引用旧的函数对象,它仍然会在内存中func。您的字典将为旧功能和新功能提供单独的条目,这可能对您更有效,因为这意味着您不会获得附加到旧功能的陈旧结果。您甚至可以使用您的字典作为后门来检索旧版本的函数。只需枚举calculation_cache.keys() 即可将其取回。

于 2012-09-07T04:53:41.270 回答