【问题标题】:How do I invalidate @cached_property in django如何在 django 中使 @cached_property 无效
【发布时间】:2014-06-22 18:18:23
【问题描述】:

我目前在模型类上使用@cached_property,我想在保存时将其删除,以便在下次调用时重新填充。我该怎么做呢? 示例:

class Amodel():
    #...model_fields....

    @cached_property
    def db_connection(self):
        #get some thing in the db and cache here


instance = Amodel.objects.get(id=1)
variable = instance.db_connection

Amodel.objects.select_for_update().filter(id=1).update(#some variable)
#invalidate instance.db_connection
#new_variable = instance.db_connection

谢谢

【问题讨论】:

    标签: python django django-models django-views


    【解决方案1】:

    只需删除它作为文档says。这将导致在下次访问时重新计算。

    class SomeClass(object):
        
        @cached_property
        def expensive_property(self):
             return datetime.now()
    
    obj = SomeClass()
    print obj.expensive_property
    print obj.expensive_property # outputs the same value as before
    del obj.expensive_property
    print obj.expensive_property # outputs new value
    

    对于 Python 3,del 的用法相同。下面是一个 try/except 块的示例。

    try:
        del obj.expensive_property 
    except AttributeError:
        pass 
    

    【讨论】:

    • 请注意,如果该属性尚未被访问/缓存,这将生成一个 AttributeError。在这种情况下,尝试将其包装起来,但 AttributeError 除外。
    • 好消息@dalore; django 开发人员的糟糕样板:(
    • 请注意,如果您在班级内无效,您将使用del self.__dict__['expensive_property']...除非有我不知道的非dunder方式。
    • @cached_property 绝对不是有点糟糕的样板!这是对实例字典和描述符协议之间相互作用的简洁使用。如果你在属性被调用/缓存之前尝试delattr,那么它会抛出AttributeError,这是完全有道理的,这是标准的python行为。这很有意义,因为cached_property 精确地将属性 写入 实例字典,而不是在第一次运行后调用自身。花点时间研究一下,你会发现这是一段既甜美又经济的代码。
    • 链接改为docs.djangoproject.com/en/3.0/ref/utils/…;无法编辑。
    【解决方案2】:

    我创建了一个 Django 模型 mixin,它在调用 model.refresh_from_db() 时使模型上的所有 @cached_property 属性无效。您也可以使用 model.invalidate_cached_properties() 使缓存的属性无效。

    from django.utils.functional import cached_property
    
    
    class RefreshFromDbInvalidatesCachedPropertiesMixin():
    
        def refresh_from_db(self, *args, **kwargs):
            self.invalidate_cached_properties()
            return super().refresh_from_db(*args, **kwargs)
    
        def invalidate_cached_properties(self):
            for key, value in self.__class__.__dict__.items():
                if isinstance(value, cached_property):
                    self.__dict__.pop(key, None)
    

    https://gitlab.com/snippets/1747035

    Thomas Baden 的回答启发。

    【讨论】:

    • 如果您使用from functools import cached_property,它将无法正常工作。所以我建议,检查多个字段类型:if isinstance(value, (cached_property, django_cached_property)):
    【解决方案3】:

    由于正在进行的开发而进行了大量编辑...现在支持给定 cached_property 的多个标签。

    我遇到了类似的问题,其中我有一组相关的 cached_property 对象,它们都需要同时失效。我以这种方式解决了它:

    1. 扩展cached_property 以接受标签值并包含装饰器类方法:

      def __init__(self, func, *tags):
          self.func = func
          self.tags = frozenset(tags)
      
      @classmethod
      def tag(cls *tags):
          return lambda f: cls(f, *tags)
      
    2. 在我的其他对象中,使用我的新 cached_property.tag 装饰器类方法来定义标记的 cached_property 方法:

      @cached_property.tag("foo_group")
      def foo(self):
          return "foo"
      
    3. 在我使用新装饰器的对象上,编写一个方法,通过遍历实例化对象类的__dict__,使所有带有命名标记的cached_property 值无效。这可以防止意外调用所有 cached_property 方法:

      def invalidate(self, tag):
          for key, value in self.__class__.__dict__.items():
              if isinstance(value, cached_property) and tag in value.tags:
                  self.__dict__.pop(key, None)
      

    现在,为了使无效,我只需调用myobject.invalidate("foo_group")

    【讨论】:

      【解决方案4】:

      如果你不想使用tryexcept,而且写的行数也少,可以使用:

      if (hasattr(obj, "expensive_property")):
          delattr(obj, "expensive_property")
      

      或者:

      if (hasattr(obj, "expensive_property")):
          del obj.expensive_property
      

      会删除缓存的属性,下次访问时会重新计算。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-14
        • 1970-01-01
        • 2015-09-22
        • 2018-01-02
        • 2011-12-14
        相关资源
        最近更新 更多