【问题标题】:How to cache a property at class level in python?如何在python中缓存类级别的属性?
【发布时间】:2019-04-02 10:22:38
【问题描述】:

所以我正在研究一个Customer class,它应该是一些其他类的包装器,这些类从服务器和在线检索有关特定客户的信息,如下所示。

class Customer:
    def __init__(self, name):
        self.name = name

    @property
    @lru_cache()
    def online_info(self):
       print('retrieving customer online info')
       return Online().search_result(for=self)

    @property
    @lru_cache()
    def server_info(self):
      print('retrieving customer server info')
      return Server().search_result(for=self)

在线和服务器调用必须是@property 装饰。我面临的问题是尝试缓存 online_infoserver_info 调用时。缓存必须以某种方式处于类级别,以便即使实例化新闻客户,lru_cache 也会记住来自其他实例化的同名调用的先前调用。注意我的打印声明。这是我想要实现的行为:

>>> cutomer1 = Customer('John')
>>> customer1.online_info
retrieving customer online info
John Smith has the following info bla bla bla ....

>>> cutomer2 = Customer('John')
>>> customer2.online_info # this one will not be calling the function, lru_cache will return the value saved from customer1.online_info
John Smith has the following info bla bla bla ....

有人可以解释我是如何实现这种行为的吗?这可能吗?

【问题讨论】:

  • 如果我做Customer('not John').online_info怎么办?那也应该使用缓存的值吗?
  • @Aran-Fey 在这种情况下不会,因为之前从未调用过“no John”
  • 您不能在属性上使用lru_cache,或者至少,即使您的装饰器订单正确(其中和现在一样不正确)。 lru_cache 的工作原理是查看 函数的参数,而这里你所拥有的只是......什么都没有,因为属性不接受参数。
  • @Aran-Fey 如果您有另一个 Customer('no John').online 信息,在初始的 'no John' 之后,第二个 'no John' 的值将从缓存而不是来自属性调用
  • @MartijnPieters 这不对。有self 参数。如果交换了装饰器,则代码可以正常工作。

标签: python python-3.x class caching properties


【解决方案1】:

我建议不要在类上缓存属性值,而是为每个“John”重新使用相同的 Customer 实例,以便

>>> Customer('John') is Customer('John')
True

这将使Customer 成为一个单身人士。在这个问题中可以找到很多单例实现:Creating a singleton in Python。借用其中一个实现给我们一个伪单例元类,如下所示:

class NameSingleton(type):
    def __init__(cls, *args, **kwargs):
        cls._instances = {}

    def __call__(cls, name, *args, **kwargs):
        try:
            return cls._instances[name]
        except KeyError:
            instance = super().__call__(name, *args, **kwargs)
            cls._instances[name] = instance
            return instance

使用它作为Customer 的元类,你就完成了:

class Customer(metaclass=NameSingleton):
    def __init__(self, name):
        self.name = name

    ...

演示:

>>> Customer('John') is Customer('John')
True
>>> Customer('John') is Customer('not John')
False

【讨论】:

  • 这是一个有趣的构建。但是,我有点不愿意使用此代码,因为在链上,我正在从 pandas 数据框创建多个客户,然后在多个线程上调用 server_info 和 online_info,我担心使用单例和多线程时,它们可能会相互绊倒
  • @callmeGuy 好吧,可能发生的最糟糕的事情是两个线程同时创建一个 John,并最终得到两个不同的 John 实例。但是,每个缓存机制都有这个问题,无论您是缓存 Customer 实例还是属性值。如果有问题,您可以将Customer 构造函数包装在threading.Lock 中。
  • 所以如果我同时创建 2 个客户并且他们恰好具有相同的值,那么缓存将毫无用处。有没有全局缓存之类的东西?
  • @callmeGuy 这一个全局缓存,但它不是线程安全的。它只需要一把锁。
猜你喜欢
  • 1970-01-01
  • 2016-09-26
  • 1970-01-01
  • 1970-01-01
  • 2019-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多