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

用户名

我正在研究一个表示具有大量相关数据的对象的类。我将这些数据存储在名为metadata的字典类属性中表示形式可以是:

{'key1':slowToComputeValue, 'key2':evenSlowerToComputeValue}

在某些情况下,值的计算非常慢,因此我想做的是使用“ getter”函数,首先尝试从元数据字典中获取值仅在KeyError上(即,当getter尝试获取尚不存在的键的值时),才应计算该值(并将其添加到字典中,以便在下次调用getter时进行快速访问)。

我从一个简单的开始:

try:
    return self.metadata[requested_key]
except KeyError:
    #Implementation of function

由于班上有许多吸气剂,我开始认为这头三行代码可以由装饰器处理。但是我在使这项工作中遇到问题。问题是我需要将元数据字典从类实例传递给装饰器。我发现了一些类似这样的教程和帖子,它们表明可以将参数发送到封闭函数,但是我遇到的困难是向其发送类实例化属性元数据(如果我发送字符串值,则可以工作) )。

我尝试的一些示例代码在这里:

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 = {}

我遇到的错误通常是无法自定义的,但是我尝试了参数放置,装饰器放置等的各种组合,但均未成功。

所以我的问题是:

  1. 我该如何工作?
  2. 装饰器是实现我要完成的任务的合适方法吗?
阿什维尼乔杜里(Ashwini Chaudhary)

是的,装饰器是一个很好的用例。例如Django,已经包含了类似的东西,称为cached_property

基本上,它所做的就是,当第一次访问该属性时,它将__dict__以与函数相同的名称将数据存储在实例的dict()中。当我们稍后获取相同的属性时,简单地从实例字典中获取值。

Acached_property是一个非数据描述符因此,一旦在实例的字典中设置了键,对属性的访问将始终从那里获取值。

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_full_name()到目前为止,我们面向公众的客户一直使用某物(例如)作为方法,但我们意识到将它用作属性(仅full_name是更合适的,在这种情况下,保持该方法完好无损,但将其标记为已弃用,并从现在开始建议用户使用新属性。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章