python - 类装饰器自动更新磁盘上的属性字典?

标签 python class decorator

我正在开发一个项目,其中有许多自定义类可以与用户系统上的各种数据集合进行交互。这些类仅具有作为面向用户的属性的properties。其中一些属性是相当资源密集型的,因此我只想运行生成代码一次,并将返回值存储在磁盘上(即缓存它),以便在后续运行时更快地检索。就目前而言,这就是我实现这一目标的方式:

def stored_property(func):
    """This ``decorator`` adds on-disk functionality to the `property`
    decorator. This decorator is also a Method Decorator.

    Each key property of a class is stored in a settings JSON file with
    a dictionary of property names and values (e.g. :class:`MyClass`
    stores its properties in `my_class.json`). 
    """
    @property
    @functools.wraps(func)
    def func_wrapper(self):
        print('running decorator...')
        try:
            var = self.properties[func.__name__]
            if var:
                # property already written to disk
                return var
            else:
                # property written to disk as `null`
                return func(self)
        except AttributeError:
            # `self.properties` does not yet exist
            return func(self)
        except KeyError:
            # `self.properties` exists, but property is not a key
            return func(self)
    return func_wrapper

class MyClass(object):
    def __init__(self, wf):
        self.wf = wf
        self.properties = self._properties()

    def _properties(self):
        # get name of class in underscore format
        class_name = convert(self.__class__.__name__)
        # this is a library used (in Alfred workflows) for interacted with data stored on disk
        properties = self.wf.stored_data(class_name)
        # if no file on disk, or one of the properties has a null value
        if properties is None or None in properties.values():
            # get names of all properties of this class
            propnames = [k for (k, v) in self.__class__.__dict__.items()
                         if isinstance(v, property)]
            properties = dict()
            for prop in propnames:
                # generate dictionary of property names and values
                properties[prop] = getattr(self, prop)
            # use the external library to save that dictionary to disk in JSON format
            self.wf.store_data(class_name, properties,
                               serializer='json')
        # return either the data read from file, or data generated in situ
        return properties

    #this decorator ensures that this generating code is only run if necessary
    @stored_property
    def only_property(self):
        # some code to get data
        return 'this is my property'

这段代码完全按照我的需要工作,但它仍然迫使我手动将 _properties(self) 方法添加到我需要此功能的每个类(目前,我有 3 个)。我想要的是一种将此功能“插入”到我喜欢的任何类中的方法。我认为类装饰器可以完成这项工作,但尽我所能,我不太明白如何解决它。

为了清楚起见(以防装饰器不是获得我想要的东西的最佳方式),我将尝试解释我所追求的整体功能。我想编写一个包含一些属性的类。这些属性的值是通过不同程度的复杂代码生成的(在一个实例中,我正在搜索某个应用程序的首选项文件,然后搜索 3 个不同的首选项(其中任何一个可能存在也可能不存在)并确定最佳的单个首选项这些偏好的结果)。我希望属性代码的主体仅包含用于查找数据的算法。但是,我不想每次访问该属性时都运行该算法代码。一旦我生成了一次值,我想将其写入磁盘,然后在所有后续调用中简单地读取该值。但是,我不希望每个值都写入自己的文件;我希望将单个类的所有属性的所有值的字典写入一个文件(因此,在上面的示例中,my_class.json 将包含一个带有一个键、值的 JSON 字典一对)。当直接访问该属性时,应首先检查该属性是否已存在于磁盘上的字典中。如果是,只需读取并返回该值即可。如果存在,但值为空,则尝试运行生成代码(即属性方法中实际编写的代码),看看现在是否可以找到它(如果没有,该方法将返回 None 并将再次写入文件)。如果字典存在并且该属性不是键(我当前的代码并没有真正使这成为可能,但安全总比抱歉好),运行生成代码并添加键、值对。如果字典不存在(即在类的第一次实例化时),则运行所有属性的所有生成代码并创建 JSON 文件。理想情况下,代码能够更新 JSON 文件中的一个属性,而无需重新运行所有生成代码(即再次运行 _properties())。

我知道这有点奇怪,但我需要速度、人类可读的内容和优雅的代码。我真的不必在我的目标上妥协。希望我想要的描述足够清楚。如果没有,请在评论中告诉我哪些地方没有意义,我会尽力澄清。但我确实认为类装饰器可能可以帮助我实现这一目标(本质上是通过将 _properties() 方法插入到任何类中,在实例化时运行它,并将其值映射到 properties code> 类的属性)。

最佳答案

也许我遗漏了一些东西,但您的 _properties 方法似乎并不特定于给定类所具有的属性。我将它放在一个基类中,并让每个带有 @stored_property 方法的类都成为它的子类。那么您就不需要重复 _properties 方法。

class PropertyBase(object):
    def __init__(self, wf):
        self.wf = wf
        self.properties = self._properties()

    def _properties(self):
        # As before...

class MyClass(PropertyBase):
    @stored_property
    def expensive_to_calculate(self):
        # Calculate it here

如果由于某种原因你不能直接子类化PropertyBase(也许你已经需要一个不同的基类),你可以使用mixin。如果失败,请让 _properties 接受实例/类和工作流对象,并在 __init__ 中为每个类显式调用它。

关于python - 类装饰器自动更新磁盘上的属性字典?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26766319/

相关文章:

未强制执行 C++ 虚函数

java - 多级泛型

python - 将类装饰器传播到继承的类

python - Google App Engine 使用数组将模板变量传递到 View 中

python - 将多个返回值附加到 Python 中的不同列表

python - 南迁 myapp 0001 --fake

python - 使用 try 语句从嵌套 for 循环写入文件

javascript - 使用 Javascript 将类和 ID 添加到 header

java - 装饰器模式 : value of field of concrete component is the value of field from component, 但为什么呢?

c - 如何在c中的另一个函数之前和之后执行公共(public)代码?