【问题标题】:why python3 lru_cache doesn't offer an put and query method?为什么 python3 lru_cache 不提供放置和查询方法?
【发布时间】:2023-03-13 14:55:01
【问题描述】:

这是我的情况,is_exist 函数有性能问题。

def is_exist(link :str) -> bool:
    if query_db(link) is True:
         return True
    return False

def process(link: str)
   if is_exist(link) is True:
       return
   # do something
   # put result to database

LRU 缓存是一个很好的解决方案,但经过数小时的努力,我发现 lru_cache 不符合我的要求。我想要的是这样的:

def is_exist(link :str) -> bool:
    if query_db(link) is True:
         return True
    return False

def process(link: str)
   if lru_cache.query(link) is True:
      return

   if is_exist(link) is True:
       lru_cache.add(link)
       return
   # do something
   # put result to database

functiontools 中的 LRU Cache 是一个装饰器。它没有查询方法。如果我使用@lru_cache 装饰is_exist 某些链接会在is_exist 返回false 时重复处理。

PS:把结果放到数据库是异步的

【问题讨论】:

  • 你能更清楚你真正想要发生的事情吗? @sophros 的答案是在此处适当使用 @lru_cache,但这似乎不是您想要的。你想装饰 both 函数吗(我怀疑它,因为它没有返回任何有用的东西)?
  • 我想问题是,如果第一次从 is_exists 返回 False,则使用 lru_cache 即使将链接写入数据库,该值也将始终返回 - 即 OP 需要一种方法来使 lrucache 无效当链接最初不存在但随后已在数据库中创建时。
  • @AndrewJaffe 我已经评论了 sophros 的回答。
  • 通过没有两个函数来避免这个问题 - 组合成一个 lru_cache-d 函数,看我的回答
  • 您的标题询问为什么该功能不存在。您的描述询问如何解决它。你对这两者中的哪一个真正感兴趣?

标签: python python-3.x caching lru


【解决方案1】:

functools.lru_cache 仅缓存结果,但不缓存异常。这意味着函数应该在以后必须重新评估的任何情况下引发异常。

@lru_cache
def is_exist(link: str) -> bool:
    if query_db(link):
         return True   # added to cache
    raise LookupError  # ignored by cache

这允许客户端函数检查 - 使用缓存 - 但仍然插入丢失的条目。

def process(link: str)
   try:
       is_exist(link)
   except LookupError:
       # do something
       # put result to database

【讨论】:

    【解决方案2】:

    您需要对链接进行测试,如果它不存在,则在同一个调用中创建它 - 可能最简单的方法是摆脱缓存的 is_exists 并缓存 process() 函数,但将其重命名为例如确保链接:

    @lru_cache
    def ensure_link(link: str)
       if query_db(link) is True:
           return
       # do something
       # put result to database
       return
    

    要么这样,要么自己在函数属性或类似属性中进行缓存 - 这不是很困难

    【讨论】:

    • 你的回答帮助了我。但是在执行 ensure_link 失败时可能会发生。在接受时由我自己实现缓存。如果python提供带有查询和放置方法的lru_cache容器会很好。
    • 您可以只使用dict 来达到此目的,除非您认为您实际上要填充缓存。实现问题就是它的存储位置。全球?函数属性?
    【解决方案3】:

    lru_cache 的典型用法

    我不确定你为什么不申请 lru_cache 作为装饰器:

    @lru_cache(maxsize=None)
    def is_exist(link :str) -> bool:
        if query_db(link) is True:
             return True
        return False
    

    只要对is_exist 的重复调用将使用相同的参数link,缓存就会有效。

    请注意maxsize=None,它将确保缓存大小的限制远大于默认的 128(并且仅受 RAM 大小限制)。

    自己的缓存实现

    lru_cache 不提供根据函数的返回值缓存结果的灵活性。但是,您可以使用set 轻松实现它:

    cache = set()
    
    def is_exist(link :str) -> bool:
        if query_db(link) is True:
             return True
        return False
    
    def process(link: str)
       if link in cache:
          return True
    
       if is_exist(link) is True:
           cache.add(link)
           return
    

    【讨论】:

    • 当 query_db 返回 False 如果再次出现相同的链接,则需要再次处理该链接。
    • @shaocong 是否要同时拥有is_existis_processed 以避免重复处理?
    猜你喜欢
    • 1970-01-01
    • 2010-11-08
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 2014-05-31
    • 2012-04-15
    相关资源
    最近更新 更多