@properties装饰器缓存是否会产生结果?

时间:2013-12-17 18:27:24

标签: python performance python-3.x

我的IDE已经"更正了" 我的代码,用于将函数(和其他代码)转换为属性。我担心这可能效率低下。

@property
def output_all_children(self):
    lh = ListHolder()
    traverse_directories(self.start_directory, lh)
    return lh.internal_list

这会带来一些繁重的I / O提升并需要一些时间。我现在想知道由于效率原因这是不正确的。我想知道结果是否像我希望的那样缓存。

如果多次访问此属性,它是否会重建并每次都返回lh.internal_list? 我会通过使用类级变量来更正它,并在更改self.start_directory时更新它。

我看过这个: How to create decorator for lazy initialization of a property并且它指的是只读属性,而我的将是更新的属性

请不要盲目信任IDE。我知道,这种想法促成了这个问题。

2 个答案:

答案 0 :(得分:3)

不,属性只是将属性访问转换为函数调用。不会发生自动缓存。

换句话说,语法instance.output_all_children会被翻译为instance.output_all_children()

您可以轻松地向属性方法添加一些缓存,或者您可以重复使用您在问题中链接的代码;如果您愿意,只需替换__set__方法来处理属性赋值。

添加一些缓存:

_output_all_children = None

@property
def output_all_children(self):
    if self._output_all_children is None:
        lh = ListHolder()
        traverse_directories(self.start_directory, lh)
        self._output_all_children = lh.internal_list
    return self._output_all_children

答案 1 :(得分:1)

实际上,每次都会调用该属性。这不是一个很好的财产使用;调用者希望它不会比属性访问花费更长的时间。

Pyramid中有一个名为reify的可爱装饰器,它第一次调用该函数,然后将该值设置为对象上的同名属性,以便下次使用先前计算的值。 / p>

如果您不想仅仅为一个装饰器使用Pyramid,您可以使用我写的这个版本(它的工作方式类似于金字塔,但我是独立编写的):

import functools

def reify(func):

    class Descriptor(object):
        def __get__(self, inst, type=None):
            val = func(inst)
            setattr(inst, func.__name__, val)
            return val

    return functools.wraps(func)(Descriptor())

为了使它与您的用例完美配合,只需要del当您需要重新计算实例时该属性;

@reify
def all_children(self):
    lh = ListHolder()
    traverse_directories(self.start_directory, lh)
    return lh.internal_list

# for internal use only, call when a cached all_children may no longer be valid
def _invalidate_all_children(self):
    try:
        del self.all_children
    except AttributeError:
        pass