docs: def cached_property(func):
      """Create read-only property that caches its function's value"""
      @functools.wraps(func)
      def cached_func(self):
          cacheattr = '_{}'.format(func.func_name)
          try:
              return getattr(self, cacheattr)
          except AttributeError:
              value = func(self)
              setattr(self, cacheattr, value)
              return value
      return property(cached_func)