def cached_property(fn):
    """
    Decorator that turns the given method into a cached property.
    To clear the cache, delete self._cache[fn].
    The preferred way of clearing the cache is by using an
    "@invalidates_cache" decorator on another method.
    """
    def wrapped(self, *args, **kwargs):
        if fn not in self._cache:
            self._cache[fn] = fn(self, *args, **kwargs)
        return self._cache[fn]
    return property(wrapped)