如何在Python中缓存类级别的属性?

问题描述 投票:0回答:2

所以我正在开发一个

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_info
server_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 ....

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

python python-3.x class caching properties
2个回答
2
投票

我建议为每个“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

0
投票

假设您想要使用您提供的调用模式:您需要实例的“属性”属性,这些属性在获取时会返回由

self.name
标识的客户的信息。但您希望在类范围内缓存客户信息,因此如果您创建具有相同
self.name
的两个实例,则两个实例之间不会重复工作。

在我看来,你想要的是一个“属性”属性,其 fget 函数调用类方法,并且类方法缓存其结果。

此代码实现了该模式

import functools

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

    @property
    def info(self):
       print('retrieving info 1 for', self.name)
       return Customer.info_implement(self.name)

    @classmethod
    @functools.cache
    def info_implement(cls, name):
       print('retrieving info 2 for', name)
       return "info for " + name

customer1 = Customer('John')
print("customer1 info", customer1.info)
print("customer1 info", customer1.info)

customer2 = Customer('John')
print("customer2 info", customer2.info)
print("customer2 info", customer2.info)

并产生这个输出

retrieving info 1 for John
retrieving info 2 for John
customer1 info info for John
retrieving info 1 for John
customer1 info info for John
retrieving info 1 for John
customer2 info info for John
retrieving info 1 for John
customer2 info info for John
© www.soinside.com 2019 - 2024. All rights reserved.