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 takestimeout
argument in secondscached_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
Post a Comment