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
运算符查看答案是否已计算的答案必须考虑哈希属性才能正常工作。我提到这一点是因为问题中的因式分解可能会返回一个矩阵;如果矩阵作为列表实现,则它不可记忆。您还可以将矩阵实现为元组(主要是不可变列表),这样您就可以绕过这一限制。在这种约束下,您的解决方案会有所帮助吗?