Python装饰器在类中获取或设置字典值
我正在处理一个类,该类表示具有大量关联数据的对象。我将这些数据存储在一个名为metadata的字典类属性中。代表可以是:Python装饰器在类中获取或设置字典值,python,caching,python-decorators,Python,Caching,Python Decorators,我正在处理一个类,该类表示具有大量关联数据的对象。我将这些数据存储在一个名为metadata的字典类属性中。代表可以是: {'key1':slowToComputeValue, 'key2':evenSlowerToComputeValue} 在某些情况下,值的计算非常慢,因此我想做的是,使用“getter”函数,首先尝试从元数据dict中获取值。只有在出现keyrerror时(即getter尝试获取尚未存在的键的值),才应计算值(并添加到字典中,以便下次调用getter时快速访问) 我从一个
{'key1':slowToComputeValue, 'key2':evenSlowerToComputeValue}
在某些情况下,值的计算非常慢,因此我想做的是,使用“getter”函数,首先尝试从元数据dict中获取值。只有在出现keyrerror时(即getter尝试获取尚未存在的键的值),才应计算值(并添加到字典中,以便下次调用getter时快速访问)
我从一个简单的例子开始:
try:
return self.metadata[requested_key]
except KeyError:
#Implementation of function
由于类中有很多getter,我开始认为前3行代码可以由decorator处理。但是,我在这方面遇到了问题。问题是我需要将元数据字典从类实例传递给decorator。我发现了一些教程和帖子,比如其中一篇,说明它是可行的可以向封闭函数发送参数,但我遇到的困难是向它发送类实例化属性元数据(如果我发送字符串值,它就会工作)
下面是我尝试的一些示例代码:
def get_existing_value_from_metadata_if_exists(metadata):
def decorator(function):
@wraps(function)
def decorated(*args, **kwargs):
function_name = function.__name__
if function_name in metadata.keys():
return metadata[function_name]
else:
function(*args, **kwargs)
return decorated
return decorator
class my_class():
@get_existing_value_from_metadata_if_exists(metadata)
def get_key1(self):
#Costly value calculation and add to metadata
@get_existing_value_from_metadata_if_exists(metadata)
def get_key2(self):
#Costly value calculation and add to metadata
def __init__(self):
self.metadata = {}
我得到的错误通常是自我定义的,但我尝试了各种参数放置、装饰器放置等组合,但没有成功
因此,我的问题是:
是的,decorator是一个很好的用例。例如,Django已经包含了类似的东西,它被称为 基本上,它所做的就是,当第一次访问该属性时,它将以与函数相同的名称将数据存储在实例的dict(
\uuuuuuu dict\uuuuu
)中。当我们稍后获取相同的属性时,它只需从实例字典中获取值
cached\u属性是一个。因此,一旦在实例的字典中设置了键,对属性的访问将始终从中获取值
class cached_property(object):
"""
Decorator that converts a method with a single self argument into a
property cached on the instance.
Optional ``name`` argument allows you to make cached properties of other
methods. (e.g. url = cached_property(get_absolute_url, name='url') )
"""
def __init__(self, func, name=None):
self.func = func
self.__doc__ = getattr(func, '__doc__')
self.name = name or func.__name__
def __get__(self, instance, cls=None):
if instance is None:
return self
res = instance.__dict__[self.name] = self.func(instance)
return res
就你而言:
class MyClass:
@cached_property
def key1(self):
#Costly value calculation and add to metadata
@cached_property
def key2(self):
#Costly value calculation and add to metadata
def __init__(self):
# self.metadata not required
使用name
参数将现有方法转换为缓存属性
class MyClass:
def __init__(self, data):
self.data = data
def get_total(self):
print('Processing...')
return sum(self.data)
total = cached_property(get_total, 'total')
演示:
>>> m = MyClass(list(range(10**5)))
>>> m.get_total()
Processing...
4999950000
>>> m.total
Processing...
4999950000
>>> m.total
4999950000
>>> m.data.append(1000)
>>> m.total # This is now invalid
4999950000
>>> m.get_total() # This still works
Processing...
4999951000
>>> m.total
4999950000
根据上面的例子,我们可以看到,只要我们知道内部数据尚未更新,就可以使用total
,从而节省处理时间。但这并不意味着get_total()
冗余,因为它可以根据数据获得正确的总计
另一个例子可能是,我们面向公众的客户机目前正在使用某种方法(比如get\u full\u name()
),但我们意识到将其用作属性更合适(只是full\u name
),在这种情况下,保持方法不变是有意义的,但要将其标记为已弃用,并从现在开始建议用户使用新属性。另一种方法是使用类“属性”,如下所示:
现在,您可以像访问类属性一样访问它:
myclass = MyClass()
print(myclass.slowToComputeValue)
你想让数据在my_class
的不同实例之间保持不变吗?不-每个实例都与一个音频文件相关。因此每个实例都有自己的一组属性。哇,这是什么魔法……它起作用了,但我仍在努力想办法。你能添加一个使用kwarg名称的示例吗?@user3535074什么你的意思是“name kwarg”?->def\uuu init\uuuuu(self,func,name=None)的“name=None”部分
myclass = MyClass()
print(myclass.slowToComputeValue)