首页 >后端开发 >Python教程 >Python 缓存可变值

Python 缓存可变值

Barbara Streisand
Barbara Streisand原创
2025-01-26 16:13:10898浏览

Python Caching mutable values

缓存极大地加速了从 CPU 级操作到数据库接口的处理速度。 缓存失效(确定何时删除缓存数据)是一项复杂的挑战。这篇文章解决了一个更简单但隐蔽的缓存问题。

这个问题潜伏了 18 个月,只有当用户偏离推荐的使用模式时才浮出水面。 该问题源于我组织内的自定义机器学习 (ML) 框架(基于 scikit-learn 构建)。 该框架频繁访问多个数据源,需要缓存层来优化性能和成本(降低 BigQuery 出口成本)。

最初使用lru_cache,但开发过程中经常访问的静态数据需要持久化缓存。 DiskCache 是一个使用 SQLite 的 Python 库,因其简单性以及与我们的 32 进程环境和 Pandas DataFrame(高达 500MB)的兼容性而被选中。 在顶部添加了一个 lru_cache 层以进行内存访问。

随着越来越多的用户尝试该框架,问题出现了。 随机报告了不正确的结果,难以一致地重现。 根本原因:就地修改缓存的 Pandas DataFrame。

我们的编码标准规定在任何处理后创建新的数据帧。 然而,有些用户出于习惯,使用inplace=True,直接修改缓存对象。 这不仅改变了它们的即时结果,还破坏了缓存的数据,影响后续请求。

为了说明这一点,请考虑这个使用字典的简化示例:

<code class="language-python">from functools import lru_cache
import time
import typing as t
from copy import deepcopy

@lru_cache
def expensive_func(keys: str, vals: t.Any) -> dict:
    time.sleep(3)
    return dict(zip(keys, vals))


def main():
    e1 = expensive_func(('a', 'b', 'c'), (1, 2, 3))
    print(e1)

    e2 = expensive_func(('a', 'b', 'c'), (1, 2, 3))
    print(e2)

    e2['d'] = "amazing"

    print(e2)

    e3 = expensive_func(('a', 'b', 'c'), (1, 2, 3))
    print(e3)


if __name__ == "__main__":
    main()</code>

lru_cache 提供参考,而不是副本。 修改e2会改变缓存的数据。

解决方案:

解决方案涉及返回缓存对象的深层副本:

<code class="language-python">from functools import lru_cache, wraps
from copy import deepcopy

def custom_cache(func):
    cached_func = lru_cache(func)

    @wraps(func)
    def _wrapper(*args, **kwargs):
        return deepcopy(cached_func(*args, **kwargs))

    return _wrapper</code>

这会增加少量开销(数据重复),但可以防止数据损坏。

要点:

  • 更深入地了解lru_cache的引用行为。
  • 遵守编码标准可以最大限度地减少错误。
  • 考虑到用户在实施过程中偏离最佳实践的情况。 坚固往往胜过优雅。

以上是Python 缓存可变值的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn