Python装饰器在类中获取或设置字典值

Python装饰器在类中获取或设置字典值,python,caching,python-decorators,Python,Caching,Python Decorators,我正在处理一个类,该类表示具有大量关联数据的对象。我将这些数据存储在一个名为metadata的字典类属性中。代表可以是: {'key1':slowToComputeValue, 'key2':evenSlowerToComputeValue} 在某些情况下,值的计算非常慢,因此我想做的是,使用“getter”函数,首先尝试从元数据dict中获取值。只有在出现keyrerror时(即getter尝试获取尚未存在的键的值),才应计算值(并添加到字典中,以便下次调用getter时快速访问) 我从一个

我正在处理一个类,该类表示具有大量关联数据的对象。我将这些数据存储在一个名为metadata的字典类属性中。代表可以是:

{'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)