Is there an easy way to memoize (and flush) properties on Python at object level?

两盒软妹~` 提交于 2021-02-08 10:20:54

问题


I'm looking for a way to cache properties of an object. In my case, I suppose the object can change over the time, so the memoized value for the property should be flushable. In pure python, I want to have a behaviour like:

class Foo:
  def __init__(self, text: str):
    self._text  = text
    self._bar = None

  def flush(self):
     self._bar = None

  def update_text(self, text: str):
     self._text = text 
     self.flush()

  @property
  def bar(self):
    if self._bar is None:
      print('Computing bar...')
      self._bar = f'Computation with "{self._text}".'
    return self._bar

foo1 = Foo('Dog')
foo2 = Foo('Fish')

print(foo1.bar)
# Computing bar...
# Computation with "Dog".

print(foo1.bar)
# Computation with "Dog".

print(foo2.bar)
# Computing bar...
# Computation with "Fish".

print(foo2.bar)
# Computation with "Fish".

foo1.update_text('Cat')

print(foo1.bar)
# Computing bar...
# Computation with "Cat".

print(foo1.bar)
# Computation with "Cat".

print(foo2.bar)
# Computation with "Fish".

Then, as you can see, I want to cache the Foo.bar property. My approach was to define a private property initialised as None and then assigned and flushed to obtain a memoized behaviour.

Now, my question is if there is some method, library, approach or technique to obtain this behaviour without the need to have a private property (imagine you have tends of memoizable properties in the class).

I was reading about the @lru_cache() decorator (and the newest @cached_property) from the Standard Library (https://docs.python.org/3/library/functools.html), but I realised that the cache_clear() method removes the memoized values for all the instances of the class.

I was thinking that one possible solution could be to use immutable objects, but that solution is not as I want, because probably there will be situations in which I only want to flush one of the properties memoization.


回答1:


Thanks to @sanyash discussion on question comments.

There is a cached_property package (https://pypi.org/project/cached-property/) that provides the requested behaviour. The example using cached_property is as follows:

from cached_property import cached_property


class Foo:

    def __init__(self, text: str):
        self._text = text

    def flush(self):
        del self.__dict__['bar']

    def update_text(self, text: str):
        self._text = text
        self.flush()

    @cached_property
    def bar(self):
        print('Computing bar...')
        return f'Computation with "{self._text}".'


foo1 = Foo('Dog')
foo2 = Foo('Fish')

print(foo1.bar)
# Computing bar...
# Computation with "Dog".

print(foo1.bar)
# Computation with "Dog".

print(foo2.bar)
# Computing bar...
# Computation with "Fish".

print(foo2.bar)
# Computation with "Fish".

foo1.update_text('Cat')

print(foo1.bar)
# Computing bar...
# Computation with "Cat".

print(foo1.bar)
# Computation with "Cat".

print(foo2.bar)
# Computation with "Fish".



回答2:


cached_property is the perfect solution to cache properties.

If you have similar concern about functions, methodtools works for each instances.

import methodtools

class Foo:
  @methodtools.lru_cache(maxsize=128)
  def bar(self, a, b, c):
    return something_using_parameters


来源:https://stackoverflow.com/questions/58471400/is-there-an-easy-way-to-memoize-and-flush-properties-on-python-at-object-level

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!