Refactor cached_property_with_ttl.
authorGeorge Sakkis <george.sakkis@gmail.com>
Tue, 21 Apr 2015 00:08:27 +0000 (03:08 +0300)
committerGeorge Sakkis <george.sakkis@gmail.com>
Tue, 21 Apr 2015 00:16:59 +0000 (03:16 +0300)
Cache (value, time) in the object's __dict__ instead of doing an extra lookup
in a '_cache' dict; also avoids the potential name clash with an unrelated
attribute named '_cache'.

cached_property.py

index f05947e10e4783ed2808ba68e044adb4e4062c16..45654b8d1ca5648d8e2791094ec38d2fad4bb618 100644 (file)
@@ -61,22 +61,16 @@ class cached_property_with_ttl(object):
     """
 
     def __init__(self, ttl=None):
-        ttl_or_func = ttl
-        self.ttl = None
-        if callable(ttl_or_func):
-            self.prepare_func(ttl_or_func)
+        if callable(ttl):
+            func = ttl
+            ttl = None
         else:
-            self.ttl = ttl_or_func
+            func = None
+        self.ttl = ttl
+        self._prepare_func(func)
 
-    def prepare_func(self, func, doc=None):
-        '''Prepare to cache object method.'''
-        self.func = func
-        self.__doc__ = doc or func.__doc__
-        self.__name__ = func.__name__
-        self.__module__ = func.__module__
-
-    def __call__(self, func, doc=None):
-        self.prepare_func(func, doc)
+    def __call__(self, func):
+        self._prepare_func(func)
         return self
 
     def __get__(self, obj, cls):
@@ -84,25 +78,30 @@ class cached_property_with_ttl(object):
             return self
 
         now = time()
+        obj_dict = obj.__dict__
+        name = self.__name__
         try:
-            value, last_update = obj._cache[self.__name__]
-            if self.ttl and self.ttl > 0 and now - last_update > self.ttl:
-                raise AttributeError
-        except (KeyError, AttributeError):
-            value = self.func(obj)
-            try:
-                cache = obj._cache
-            except AttributeError:
-                cache = obj._cache = {}
-            cache[self.__name__] = (value, now)
+            value, last_updated = obj_dict[name]
+        except KeyError:
+            pass
+        else:
+            ttl_expired = 0 < 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):
-        try:
-            del obj._cache[self.__name__]
-        except (KeyError, AttributeError):
-            pass
+        obj.__dict__.pop(self.__name__, None)
+
+    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