如何判断带有依赖关系的Python函数是否发生了变化?

How to tell whether a Python function with dependencies has changed?

提问人:rasmuse 提问时间:1/18/2016 更新时间:1/18/2016 访问量:1033

问:

tl;博士:

当且仅当函数代码及其所有依赖项自上次运行以来未更改时,如何将 Python 函数的结果缓存到磁盘并在以后的会话中使用缓存的值?

换句话说,我想制作一个 Python 缓存系统,该系统可以自动监视更改的代码。

背景

我正在尝试构建一个工具,用于自动记忆 Python 的计算结果。我希望记忆在 Python 会话之间持续存在(即以后可以在另一个 Python 实例中重用,最好甚至可以在具有相同 Python 版本的另一台机器上重用)。

假设我有一个带有某些功能的 Python 模块。假设我已经解决了序列化/标识函数参数的问题,因此我们可以假设如果它简化了任何内容,则不带任何参数。mymodulemymodule.func()mymodule.func()

还假设我保证函数及其所有依赖项都是确定性的,所以 .mymodule.func()mymodule.func() == mymodule.func()

任务

我想今天运行该函数并保存其结果(以及解决此任务所需的任何其他信息)。当我以后想要相同的结果时,我想加载缓存的结果而不是再次运行,但前提是代码及其依赖项保持不变。mymodule.func()mymodule.func()mymodule.func()

为了简化起见,我们可以假设该函数始终在新启动的 Python 解释器中运行,其脚本最小如下:

import some_save_function
import mymodule
result = mymodule.func()
some_save_function(result, 'filename')

另外,请注意,我不想过于保守。第一次运行时使用 modulefinder 模块查找所有涉及的模块,如果有任何模块发生更改,则不使用缓存可能并不难。但这违背了我的目的,因为在我的用例中,导入模块中的某些不相关的函数很可能已更改。

我看过以前的工作和工具

  • joblib 会记住与函数名称相关的结果,并保存源代码,以便我们可以检查它是否保持不变。但是,据我了解,它不检查上游函数(由 调用)。mymodule.func()
  • ast 模块为我提供了任何 Python 代码的抽象语法树,所以我想我可以(原则上)以这种方式弄清楚这一切。这有多难?我对 AST 不是很熟悉。
  • 我可以使用莳萝体内发生的所有黑魔法吗?
  • 比解决方案更琐事:IncPy,一个已完成/已死亡的研究项目,默认实现了一个 Python 解释器,始终这样做。好主意,但从未在实验室外实现过。

感谢您的任何意见!

Python 函数 缓存 内省 记忆

评论

0赞 culebrón 10/2/2017
你成功找到答案了吗?我一直在为自己构建一个类似的工具。
0赞 culebrón 10/2/2017
我基于函数代码、参数、闭包和嵌套调用进行记忆。到目前为止的问题是,当我再次运行该函数并想要检查嵌套调用时,我必须再次导入它们,这需要一些值得注意的时间,然后它们的闭包与“自然”导入时不同。这意味着我要么无法跟踪它们,要么跟踪它们,但更改全局内容(如设置)不会触发它们重新运行。
0赞 rasmuse 10/12/2017
不,我还没有解决这个问题,但我确实开始研究我自己的解决方案。我开始的路径是静态代码分析,它可能会遗漏一些边缘情况,但肯定会避免与导入等副作用相关的任何问题。另外,即使不使用 importlib 等跟踪导入,也能够捕获所有可能的边缘情况。我考虑使用的一些有趣的工具是:秃鹫 bitbucket.org/jendrikseipp/vulture pylint github.com/PyCQA/pylint pyflakes github.com/PyCQA/pyflakes
0赞 culebrón 10/12/2017
I tried using the globals/closures approach, and turned out when you import and decorate stuff on the way, things change between restarts, between the beginning and the end of a function, and even inside the calls tree. So probably makes sense to just use code + explicitly watched variables and external files, if they are read.
0赞 culebrón 10/12/2017
Another option would be making the memoizer/watcher decorator picky, and raise exceptions if a function that you decorate with it depends on closure vars. raise OperationalError("Give me functions without side effects!")

答: 暂无答案