如何在类属性中缓存API数据?

2024-04-19 01:24:23 发布

您现在位置:Python中文网/ 问答频道 /正文

我想有一个类,类似于下面的一个,在那里我可以使用属性从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"]

Tags: 数据selfapiformatdatagetreturn属性
1条回答
网友
1楼 · 发布于 2024-04-19 01:24:23

这里有一种方法可以使它看起来整洁(它可能看起来过于复杂,但实际上并不是):

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是一个只读描述符,它将缓存值和时间戳存储在_cachedict中的对象本身中,在随机生成的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

相关问题 更多 >