Python 不存在的全局字典的替代方案

Python 不存在的全局字典的替代方案,python,dictionary,Python,Dictionary,考虑 def f(x,*args): intermediate = computationally_expensive_fct(x) return do_stuff(intermediate,*args) 问题:对于同一个x,可能会使用不同的参数(x除外)调用该函数数千次,每次调用该函数都会计算中间值(Cholesky分解,成本O(n^3))。然而,原则上,如果对于每个x,中间值对于每个x只计算一次,那么该结果将被具有不同参数的f反复使用就足够了 我的想法为了解决这个问题,我尝

考虑

def f(x,*args):
    intermediate = computationally_expensive_fct(x)
    return do_stuff(intermediate,*args)
问题:对于同一个x,可能会使用不同的参数(x除外)调用该函数数千次,每次调用该函数都会计算中间值(Cholesky分解,成本O(n^3))。然而,原则上,如果对于每个x,中间值对于每个x只计算一次,那么该结果将被具有不同参数的f反复使用就足够了

我的想法为了解决这个问题,我尝试创建一个全局字典,函数在其中查找其参数x,昂贵的内容是否已经完成并存储在字典中,或者是否必须计算它:

if all_intermediates not in globals():
    global all_intermediates = {}

if all_intermediates.has_key(x):
    pass
else:
    global all_intermediates[x] = computationally_expensive_fct(x)

事实证明我不能这样做,因为globals()本身就是一个dict,不能在python中对dict进行哈希。我是一名程序员新手,如果有人能给我指出一种类似Python的方法来实现我想要的目标,我会很高兴的。

我不明白你为什么要使用
globals()
。您不必使用
globals()
,只需将计算出的值保存在自己的模块级字典中,并使用一个包装器函数来查找中间
是否已计算。大概是这样的:

computed_intermediate = {}

def get_intermediate(x):
    if x not in computed_intermediate:
        computed_intermediate[x] = computationally_expensive_fct(x)

    return computed_intermediate[x]

def f(x,*args):
    intermediate = get_intermediate(x)
    return do_stuff(intermediate,*args)
这样,每个
x
将只计算一次
fct(x)
,即第一次访问它。

解决方案 比编写装饰程序和不访问全局文件更轻量级:

def f(x, *args):
    if not hasattr(f, 'all_intermediates'):
        f.all_intermediates = {}
    if x not in f.all_intermediates:
        f.all_intermediates[x] = computationally_expensive_fct(x)
    intermediate = f.all_intermediates[x]
    return do_stuff(intermediate,*args)
变异 一种变体,它避免了
if not hasattr
,但需要在定义后将
所有中间产物设置为
f
的属性:

def f(x, *args):
    if x not in f.all_intermediates:
        f.all_intermediates[x] = computationally_expensive_fct(x)
    intermediate = f.all_intermediates[x]
    return do_stuff(intermediate,*args)
f.all_intermediates = {}
这会将
所有\u中间产物作为函数本身的属性进行缓存

解释 函数是对象,可以具有属性。因此,您可以将字典
所有中间产物
存储为函数
f
的属性。这使得函数是自包含的,这意味着您可以将其移动到另一个模块,而不用担心模块全局性。使用上面显示的变体,您需要移动
f.all_intermediates={}
以及函数


将内容放入
globals()
感觉不太对劲。我建议不要这样做

这通常通过昂贵函数上的
@memonized
装饰器来实现

在中对其进行了描述,并简要介绍了在链路损坏的情况下在此重复的内容:

import collections
import functools

class memoized(object):
   '''Decorator. Caches a function's return value each time it is called.
   If called later with the same arguments, the cached value is returned
   (not reevaluated).
   '''
   def __init__(self, func):
      self.func = func
      self.cache = {}
   def __call__(self, *args):
      if not isinstance(args, collections.Hashable):
         # uncacheable. a list, for instance.
         # better to not cache than blow up.
         return self.func(*args)
      if args in self.cache:
         return self.cache[args]
      else:
         value = self.func(*args)
         self.cache[args] = value
         return value
   def __repr__(self):
      '''Return the function's docstring.'''
      return self.func.__doc__
   def __get__(self, obj, objtype):
      '''Support instance methods.'''
      return functools.partial(self.__call__, obj)
一旦记忆了昂贵的功能,使用它是不可见的:

@memoized
def expensive_function(n):
   # expensive stuff
   return something

p = expensive_function(n)
q = expensive_function(n)
assert p is q

请注意,如果
昂贵的\u函数的结果不可散列(列表是一个常见的示例),则不会有性能提升,它仍会工作,但会像未被记忆一样工作。

这通常称为
@memoize
。我还不明白为什么答案是重新发明轮子。我不认为有必要与globals混在一起(这对代码的读者来说是不友好的)。@msw我在重新发明轮子,因为我不知道
@memoize
。谢谢你指出这一点。可能您应该将您的注释添加为答案。是的,全局变量绝对是非语法的。使用
in
运算符查看答案是否已计算的答案必须考虑哈希属性才能正常工作。我提到这一点是因为问题中的因式分解可能会返回一个矩阵;如果矩阵作为列表实现,则它不可记忆。您还可以将矩阵实现为元组(主要是不可变列表),这样您就可以绕过这一限制。在这种约束下,您的解决方案会有所帮助吗?