我正在开发一个项目,我有一些自定义类,可以与用户系统上的各种数据集进行交互。这些类仅具有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个)。我想要的是一种方法来插入"我喜欢这个功能到任何课程。我认为一个类装饰者可以完成这项工作,但尽可能地尝试,我无法弄清楚如何纠缠它。
为了清楚起见(如果装饰器不是获得我想要的最佳方式),我将尝试解释我所追求的整体功能。我想写一个包含一些属性的类。这些属性的值是通过不同程度的复杂代码生成的(在一个实例中,我搜索某个应用程序的pref文件,然后搜索3个不同的首选项(其中任何一个可能存在,也可能不存在)并确定这些偏好的最佳单一结果)。我想要物业的主体'代码仅包含用于查找数据的算法。但是,每次访问该属性时,我都不想运行该算法代码。一旦我生成了一次值,我想将它写入磁盘,然后在所有后续调用中读取它。但是,我不希望每个值写入自己的文件;我想要将一个类的所有属性的所有值的字典写入一个文件(因此,在上面的示例中,my_class.json
将包含一个带有一个键,值对的JSON字典)。直接访问属性时,应首先检查它是否已存在于磁盘上的字典中。如果是,只需读取并返回该值。如果它存在但是具有空值,则尝试运行生成代码(即实际在属性方法中编写的代码)并查看是否可以立即找到它(如果没有,该方法将返回None
并且这将再次被写入文件)。如果字典存在并且该属性不是密钥(我当前的代码并不能真正实现这一点,但更安全而不是遗憾),运行生成代码并添加密钥,值对。如果字典不存在(即在类的第一个实例化时),则运行所有属性的所有代码并创建JSON文件。理想情况下,代码将能够更新JSON文件中的一个属性,而无需重新运行所有生成代码(即再次运行_properties()
)。
我知道这有点奇怪,但我需要速度,人类可读的内容和优雅的代码。我真的不想妥协我的目标。希望,描述我想要的东西足够清楚。如果没有,请在评论中告诉我什么是没有意义的,我会尽力澄清。但我确实认为类装饰器可能会让我在那里(主要是通过将_properties()
方法插入任何类,在实例化中运行它,并将其值映射到类的properties
属性)。
答案 0 :(得分:1)
也许我错过了一些东西,但似乎你的_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__
中为每个类显式调用它。