考虑
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方式做我想做的事情,我会很高兴。
答案 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
的结果不可用(列表是一个常见示例),则不会有性能提升,它仍然有效,但就好像它没有被记忆一样。