实习不可变对象

时间:2016-07-06 00:13:41

标签: python python-3.x

我有一个程序,其中包含

形式的对象
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。

还有其他建议吗?

2 个答案:

答案 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