python - How can I cache API data in a class attribute? -


i have class, similar 1 below, can use property access data api. able cache data period of time, though, may access attribute rapidly without being ratelimited. cleanest way implement functionality?

from requests import   class githubuser:     def __init__(self, user):         self.user = user         print("{user} has {repos} public repos!".format(             user=user, repos=self.public_repos         ))      @property     def _api_data(self):         return get(             "https://api.github.com/users/{user}".format(user=self.user)         ).json()      @property     def public_repos(self):         return self._api_data["public_repos"] 

here's 1 way make neat (it may seem overcomplicated it's not really):

from uuid import uuid4 datetime import datetime  class cached_descriptor(object):     def __init__(self, func, timeout):         self.__doc__ = getattr(func, '__doc__')         self.func = func         self.uuid = str(uuid.uuid4())         self.timeout = timeout      def __get__(self, obj, cls):         if obj none:             return self         if not hasattr(obj, '_cache'):             obj._cache = {}         if self.uuid not in obj._cache:             obj._cache[self.uuid] = []         data = obj._cache[self.uuid]         = datetime.now()         if not data or (now - data[1]).total_seconds() > self.timeout:             obj._cache[self.uuid] = (self.func(obj), now)         return obj._cache[self.uuid][0]  class cached_property(object):     def __init__(self, timeout):         self.timeout = timeout      def __call__(self, func):         return cached_descriptor(func, self.timeout) 

to break down:

  • cached_property factory decorators takes timeout argument in seconds
  • cached_descriptor read-only descriptor stores cached value , timestamp in object in _cache dict, under randomly generated uuid avoid conflicts between multiple cached properties
  • upon first call, function called
  • each next call, function called if timeout has been exceeded

here's example of how works:

import time  class a(object):     def __init__(self):         self.n_f = self.n_g = 0      @cached_property(0.1)     def f(self):         self.n_f += 1         print('calling f', self.n_f)         return self.n_f      @cached_property(0.5)     def g(self):         self.n_g += 1         print('calling g', self.n_g)         return self.n_g  = a() print('f', a.f) print('g', a.g) print('f', a.f) print('g', a.g) print('sleep 0.2') time.sleep(0.2) print('f', a.f) print('g', a.g) print('sleep 0.4') time.sleep(0.4) print('f', a.f) print('g', a.g) 

which outputs

calling f 1 f 1 calling g 1 g 1 f 1 g 1 sleep 0.2 calling f 2 f 2 g 1 sleep 0.4 calling f 3 f 3 calling g 2 g 2 

Comments

Popular posts from this blog

ios - RestKit 0.20 — CoreData: error: Failed to call designated initializer on NSManagedObject class (again) -

java - Digest auth with Spring Security using javaconfig -

laravel - PDOException in Connector.php line 55: SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: YES) -