2016-05-28 3 views
2

Я хотел бы иметь класс, аналогичный приведенному ниже, где я могу использовать свойство для доступа к данным из API. Я хотел бы иметь возможность кэшировать данные в течение определенного периода времени, тем не менее, чтобы я мог быстро получить доступ к атрибуту, не будучи привязанным к ним. Каким будет самый чистый способ реализовать эту функциональность?Как кэшировать данные API в атрибуте класса?

from requests import get 


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"] 

ответ

1

Вот один из способов сделать это выглядеть аккуратным (это может показаться усложненной, но это не совсем):

from uuid import uuid4 
from 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 is 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] 
     now = 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) 

Чтобы разбить его:

  • cached_property фабрика для декораторов который принимает аргумент timeout в секундах
  • cached_descriptor - это дескриптор, предназначенный только для чтения, который хранит кешированное значение и метка времени в самом объекте в _cache Dict, под случайно сгенерированных UUID, чтобы избежать конфликтов между несколькими кэшированных свойств
  • при первом вызове, функция всегда будет называться
  • каждый следующий вызов, функция будет вызываться только если тайм-аут был превышен

Вот пример того, как это работает:

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 = 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) 

, который выводит

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 
Смежные вопросы