【问题标题】:Does the @properties decorator cache results?@properties 装饰器是否缓存结果?
【发布时间】:2013-12-17 18:27:23
【问题描述】:

我的 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。我知道,这种想法引发了这个问题。

【问题讨论】:

    标签: python performance python-3.x


    【解决方案1】:

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

    换句话说,语法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
    

    【讨论】:

      【解决方案2】:

      确实,每次都会调用该属性。这不是对财产的良好利用;调用者预计它所花费的时间不会比属性访问长多少。

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

      如果您不想将 Pyramid 仅用于一个装饰器,您可以使用我编写的这个版本(它的工作原理与 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
      

      【讨论】:

        猜你喜欢
        • 2014-05-05
        • 2020-05-12
        • 2015-07-29
        • 1970-01-01
        • 1970-01-01
        • 2015-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多