Viewing file: cached_property.py (3.81 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- coding: utf-8 -*-
__author__ = 'Daniel Greenfeld' __email__ = 'pydanny@gmail.com' __version__ = '1.3.0' __license__ = 'BSD'
from time import time import threading
class cached_property(object): """ A property that is only computed once per instance and then replaces itself with an ordinary attribute. Deleting the attribute resets the property. Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76 """ # noqa
def __init__(self, func): self.__doc__ = getattr(func, '__doc__') self.func = func
def __get__(self, obj, cls): if obj is None: return self value = obj.__dict__[self.func.__name__] = self.func(obj) return value
class threaded_cached_property(object): """ A cached_property version for use in environments where multiple threads might concurrently try to access the property. """
def __init__(self, func): self.__doc__ = getattr(func, '__doc__') self.func = func self.lock = threading.RLock()
def __get__(self, obj, cls): if obj is None: return self
obj_dict = obj.__dict__ name = self.func.__name__ with self.lock: try: # check if the value was computed before the lock was acquired return obj_dict[name] except KeyError: # if not, do the calculation and release the lock return obj_dict.setdefault(name, self.func(obj))
class cached_property_with_ttl(object): """ A property that is only computed once per instance and then replaces itself with an ordinary attribute. Setting the ttl to a number expresses how long the property will last before being timed out. """
def __init__(self, ttl=None): if callable(ttl): func = ttl ttl = None else: func = None self.ttl = ttl self._prepare_func(func)
def __call__(self, func): self._prepare_func(func) return self
def __get__(self, obj, cls): if obj is None: return self
now = time() obj_dict = obj.__dict__ name = self.__name__ try: value, last_updated = obj_dict[name] except KeyError: pass else: ttl_expired = self.ttl and self.ttl < now - last_updated if not ttl_expired: return value
value = self.func(obj) obj_dict[name] = (value, now) return value
def __delete__(self, obj): obj.__dict__.pop(self.__name__, None)
def __set__(self, obj, value): obj.__dict__[self.__name__] = (value, time())
def _prepare_func(self, func): self.func = func if func: self.__doc__ = func.__doc__ self.__name__ = func.__name__ self.__module__ = func.__module__
# Aliases to make cached_property_with_ttl easier to use cached_property_ttl = cached_property_with_ttl timed_cached_property = cached_property_with_ttl
class threaded_cached_property_with_ttl(cached_property_with_ttl): """ A cached_property version for use in environments where multiple threads might concurrently try to access the property. """
def __init__(self, ttl=None): super(threaded_cached_property_with_ttl, self).__init__(ttl) self.lock = threading.RLock()
def __get__(self, obj, cls): with self.lock: return super(threaded_cached_property_with_ttl, self).__get__(obj, cls)
# Alias to make threaded_cached_property_with_ttl easier to use threaded_cached_property_ttl = threaded_cached_property_with_ttl timed_threaded_cached_property = threaded_cached_property_with_ttl
|