docs: def memoizedproperty(func):
      """
      Decorator to cause a method to cache it's results in self for each
      combination of inputs and return the cached result on subsequent calls.
      Does not support named arguments or arg values that are not hashable.
  
      >>> class Foo (object):
      ...   _x = 1
      ...   @memoizedproperty
      ...   def foo(self):
      ...     self._x += 1
      ...     print('updating and returning {0}'.format(self._x))
      ...     return self._x
      ...
      >>> foo1 = Foo()
      >>> foo2 = Foo()
      >>> foo1.foo
      updating and returning 2
      2
      >>> foo1.foo
      2
      >>> foo2.foo
      updating and returning 2
      2
      >>> foo1.foo
      2
      """
      inner_attname = '__%s' % func.__name__
  
      def new_fget(self):
          if not hasattr(self, '_cache_'):
              self._cache_ = dict()
          cache = self._cache_
          if inner_attname not in cache:
              cache[inner_attname] = func(self)
          return cache[inner_attname]
  
      return property(new_fget)