我有一个程序,其中包含
形式的对象class MyObj(object):
def __init__(self, a, b):
self.__a = a
self.__b = b
self.cache = very_expensive_computation(a, b)
其中a,b
是不可变的,可能的参数a,b
的空间非常小。这些对象在程序执行期间不断创建并超出范围,因此不可避免地花费大量时间重新计算self.cache
相同的值a,b
。由于very_expensive_computation
非常昂贵,似乎最好避免垃圾收集这些项目并让构造函数返回对已存在对象的引用,如果可能的话,有点像字符串实习。
对我这样做的显而易见的方法似乎是向类中添加一个字典并覆盖__new__
和__init__
,以便他们检查字典并尽可能返回已存在的实例,但是同时这感觉有点令人不满意,因为它必须分别对每个类进行,因为在__init__
中检测你是否是一个真正的新对象可能会非常hacky。
还有其他建议吗?
答案 0 :(得分:4)
我会记住将结果存储在LRU缓存中的very_expensive_computation
,以保证使用的内存量的上限:
_very_expensive_computation_cache = RecentlyUsedContainer(100)
def cached_very_expensive_computation(a, b):
if (a, b) not in _very_expensive_computation_cache:
_very_expensive_computation_cache[(a, b)] = very_expensive_computation(a, b)
return _very_expensive_computation_cache[(a, b)]
RecentlyUsedContainer
可以是这个:https://github.com/shazow/unstdlib.py/blob/master/unstdlib/standard/collections_.py#L12
您还可以使用装饰器简化代码:
from unstdlib.standard.functools_ import memoized
@memoized(cache=RecentlyUsedContainer(100))
def cached_very_expensive_comptuation(a, b):
return very_expensive_computation(a, b)
请参阅:https://github.com/shazow/unstdlib.py/blob/master/unstdlib/standard/functools_.py#L59
我更喜欢将memoized版本的函数与" real"版本,因此调用者可以明确地看到他们可以获得缓存结果,并且它可以使测试更容易。不过,这主要是个人偏好。
编辑:正如评论中所指出的,Python 3附带functools.lru_cache
。
答案 1 :(得分:2)
您还可以使用类和实例变量完成此操作:
class BaseMyObj(object):
result_store = {}
class MyObj(BaseMyObj):
def __init__(self, a, b):
self.__a = a
self.__b = b
try:
self.cache = BaseMyObj.result_store[(a,b)]
except KeyError:
self.cache = very_expensive_computation(a, b)
BaseMyObj.result_store[(a,b)] = self.cache
def very_expensive_computation(a, b):
print('did calc')
return a+b
item = MyObj(2, 9)
>did calc
item2 = MyObj(2, 9)
item3 = MyObj(1, 4)
>did calc