替代不存在的全球字典

时间:2015-12-19 14:13:53

标签: 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用不同的args。

我的想法为了解决这个问题,我尝试创建一个全局字典,在该字典中,函数会查找其参数 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()本身就是一个字典,你不能在python中使用字符串。我是一名新手程序员,如果有人能指出我采用pythonic方式做我想做的事情,我会很高兴。

3 个答案:

答案 0 :(得分:2)

解决方案

比编写装饰器和不访问全局变量更轻量级:

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但需要在all_intermediates定义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 = {} 的变体:

all_intermediates

这会将all_intermediates缓存为函数本身的属性。

说明

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

将内容放入ShouldBeEquivalentTo感觉不对。我建议不要这样做。

答案 1 :(得分:1)

我不明白你为什么试图使用globals()。您可以简单地将计算值保存在您自己的模块级别字典中,并使用包装函数来查找是否已经计算globals(),而不是使用intermediate。像这样:

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)

这样computationally_expensive_fct(x)只会为每个x计算一次,即第一次访问时。{/ p>

答案 2 :(得分:1)

这通常在昂贵的函数上使用@memoized装饰器实现。

https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize进行了描述,并且在链接腐烂的情况下足够简短以便复制:

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

请注意,如果expensive_function的结果不可用(列表是一个常见示例),则不会有性能提升,它仍然有效,但就好像它没有被记忆一样。