【问题标题】:Keep persistent variables in memory between runs of Python script在 Python 脚本运行之间将持久变量保存在内存中
【发布时间】:2011-10-04 23:37:54
【问题描述】:

有没有办法将结果变量保存在内存中,这样我就不必在每次运行脚本开头时重新计算它? 每次运行我的脚本时,我都会对数据集(我从磁盘读取)进行一系列长(5-10 秒)的精确操作。 这不会是太大的问题,因为我非常擅长使用交互式编辑器在运行之间调试我的代码;但是有时交互功能并不能解决问题。

我知道我可以将结果写入磁盘上的文件,但我希望尽可能避免这样做。这应该是一个解决方案,它在我第一次运行脚本时生成一个变量,并将它保存在内存中,直到 shell 本身关闭或直到我明确告诉它失败。像这样的:

# Check if variable already created this session
in_mem = var_in_memory() # Returns pointer to var, or False if not in memory yet
if not in_mem:
    # Read data set from disk
    with open('mydata', 'r') as in_handle:
        mytext = in_handle.read()
    # Extract relevant results from data set
    mydata = parse_data(mytext)
    result = initial_operations(mydata)
    in_mem = store_persistent(result)

我有一个暗示 shelve 模块可能是我在这里寻找的,但看起来为了打开一个搁置变量我必须为持久对象指定一个文件名,所以我'不确定这是否正是我想要的。

关于让搁置做我想做的事的任何提示?有什么替代的想法吗?

【问题讨论】:

  • 人们经常使用 Jupyter 笔记本来做这件事

标签: python variables memory


【解决方案1】:

您可以使用reload 全局函数重新执行主脚本的代码来实现类似的效果。您将需要编写一个包装器脚本来导入您的主脚本,询问它要缓存的变量,在包装器脚本的模块范围内缓存该变量的副本,然后在您需要时(当您在标准输入上按 ENTER 或其他),它调用reload(yourscriptmodule),但这次将缓存对象传递给它,以便您的脚本可以绕过昂贵的计算。这是一个简单的例子。

wrapper.py

import sys
import mainscript

part1Cache = None
if __name__ == "__main__":
    while True:
        if not part1Cache:
            part1Cache = mainscript.part1()
        mainscript.part2(part1Cache)
        print "Press enter to re-run the script, CTRL-C to exit"
        sys.stdin.readline()
        reload(mainscript)

ma​​inscript.py

def part1():
    print "part1 expensive computation running"
    return "This was expensive to compute"

def part2(value):
    print "part2 running with %s" % value

wrapper.py 运行时,您可以编辑mainscript.py,将新代码添加到part2 函数,并能够针对预先计算的part1Cache 运行您的新代码。

【讨论】:

  • 我会考虑添加一个异常处理程序,您可以在其中运行外部源代码。
  • 如果 mainscript.py 的依赖更新了会发生什么?我需要明确地重新加载它吗?
  • 将变量存储在 os.environ 中不够吗?
  • 不是没有重新加载,没有。当您的 python 进程死亡时,环境将被清除。但是使用上述机制,您可以将数据存储在 os.environ 中,但现在您将数据暴露在进程之外,并且仅限于一定长度的字符串。通过使用简单的 Python 变量,您可以避免这些限制。
  • 优秀的答案!对于 Python3,在 wrapper.py 中包含:“from importlib import reload”
【解决方案2】:

要将数据保存在内存中,进程必须保持运行。内存属于运行脚本的进程,而不是 shell。 shell 不能为你保存内存。

因此,如果您想更改代码并保持流程运行,则必须在模块更改时重新加载它们。如果内存中的任何数据是更改的类的实例,则必须找到一种方法将其转换为新类的实例。这有点乱。没有多少语言擅长这种热补丁(我想到了 Common Lisp),而且有很多可能出错。

【讨论】:

  • 非常感谢您提供的信息丰富的回答。很高兴知道为什么某个特定的解决方案不能按我想要的方式工作,感谢您的解释。
【解决方案3】:

如果您只想为将来的会话保留一个对象(或对象图),那么搁置模块可能是矫枉过正。只需腌制您关心的对象。如果您没有 pickle 文件,请执行这项工作并保存 pickle,如果有,请加载 pickle 文件。

import os
import cPickle as pickle

pickle_filepath = "/path/to/picklefile.pickle"

if not os.path.exists(pickle_filepath):
    # Read data set from disk
    with open('mydata', 'r') as in_handle:
        mytext = in_handle.read()
    # Extract relevant results from data set
    mydata = parse_data(mytext)
    result = initial_operations(mydata)
    with open(pickle_filepath, 'w') as pickle_handle:
        pickle.dump(result, pickle_handle)
else:
    with open(pickle_filepath) as pickle_handle:
        result = pickle.load(pickle_handle)

【讨论】:

  • Pickle 也往往比 shelve 快
  • 这个答案在技术上仍然是正确的,但我也建议使用 JSON 来读取和写入缓存文件。如果您稍后决定更改语言,它也是人类可读和可移植的。我不知道 JSON 是否比 pickle“更快”(但实际上它不会妨碍其他性能良好的代码)
【解决方案4】:

Python 的搁置是一种用于腌制(序列化)对象的持久性解决方案,并且是基于文件的。优点是它直接存储 Python 对象,这意味着 API 非常简单。

如果您真的想避免使用磁盘,您正在寻找的技术是“内存数据库”。存在多种替代方案,请参阅此 SO 问题:in-memory database in Python

【讨论】:

  • 谢谢,看起来很适合其他应用程序,但对于我的需求来说似乎有点太强大了。
【解决方案5】:

这是一个依赖于操作系统的解决方案...

$mkfifo inpipe

#/usr/bin/python3
#firstprocess.py
complicated_calculation()
while True:
 with open('inpipe') as f:
  try:
   print( exec (f.read()))
  except Exception as e: print(e)

$./first_process.py &
$cat second_process.py > inpipe

这将允许您在第一个过程中更改和重新定义变量,而无需复制或重新计算任何内容。与多处理、memcached、pickle、搁置模块或数据库相比,它应该是最有效的解决方案。

如果你想在你的编辑器或 IDE 中迭代地编辑和重新定义 second_process.py 直到你没有每次都等待第一个进程(例如初始化一个大的字典等)执行,这真的很好你做出改变。

【讨论】:

  • 谢谢,我想使用这样的东西作为 HTTP 服务器的轻量级替代方案,以便在 localhost 上进行计算以从另一种语言调用。这样我可以防止每个请求重复模块加载开销。不过我想知道,当另一端没有放入任何东西时,with open('inpipe') as f 会做什么?
【解决方案6】:

奇怪的是,这里早期的答案都没有提到简单的文本文件。 OP 说他们不喜欢这个想法,但是由于这正在成为可能没有该约束的重复项的规范,因此值得一提。如果您只需要在脚本调用之间保留一些文本,请将其保存在常规文本文件中。

def main():
    # Before start, read data from previous run
    try:
        with open('mydata.txt', encoding='utf-8') as statefile:
            data = statefile.read().rstrip('\n')
    except FileNotFound:
        data = "some default, or maybe nothing"

    updated_data = your_real_main(data)

    # When done, save new data for next run
    with open('mydata.txt', 'w', encoding='utf-8') as statefile:
        statefile.write(updated_data + '\n')

这很容易扩展到更复杂的数据结构,但您可能需要使用标准结构化格式,如 JSON 或 YAML(用于将具有树状结构的数据序列化为文本)或 CSV(用于列矩阵和包含文本和/或数字的行)。

归根结底,shelvepickle 只是同一想法的泛化版本;但如果您的需求不大,那么您可以在常规文本编辑器中检查和更新这种简单文本格式的好处,并使用无处不在的标准工具进行阅读和操作,并且可以在不同的 Python 版本甚至其他编程语言之间轻松复制和共享以及版本控制系统等,非常引人注目。

顺便说一句,字符编码问题是您需要计划的复杂问题;但在当今时代,只需对所有文本文件使用 UTF-8。

另一个需要注意的是,初学者经常对保存文件的位置感到困惑。一个常见的约定是将其保存在调用用户的主目录中,尽管这显然意味着多个用户不能共享此数据。另一种是将其保存在共享位置,但这需要管理员单独授予对该位置的写访问权限(我猜在 Windows 上除外;但这会带来其他问题的构造板块)。

主要缺点是,如果您需要多个进程快速连续更新文件,则文本很脆弱,如果您有大量数据并且需要经常更新其中的一部分,则处理速度很慢。对于这些用例,可以查看数据库(可能从强大且灵活的 SQLite 开始,并包含在 Python 标准库中;如果您有企业级需求,可以扩展到 Postgres 等)。

当然,如果您需要存储原生 Python 结构,shelvepickle 仍然存在。

【讨论】:

  • 对于军工企业联合体冗余部门的企业企业部门,我应该也许可能确实可能确实一定要肯定提到并请记住不要忘记还包括 XML,它是企业和企业军工联合体冗余部门的企业企业部门的最爱。
  • 他明确表示不想写入磁盘
  • @Jules 前两句话解释了我为什么要在这里发布。
【解决方案7】:

您可以这样做,但您必须使用 Python shell。换句话说,用于启动 Python 脚本的 shell 必须是 Python 进程。然后,任何全局变量或类将一直存在,直到您关闭 shell。

看看cmd 模块,它可以很容易地编写一个shell 程序。您甚至可以安排将任何未在您的 shell 中实现的命令传递给系统 shell 以执行(无需关闭您的 shell)。然后你必须实现某种命令,例如prun,它使用runpy 模块运行Python 脚本。

http://docs.python.org/library/runpy.html

您需要使用 init_globals 参数将您的特殊数据传递到程序的命名空间,最好是字典或单个类实例。

【讨论】:

  • 这显然在重启后是不可行的。
【解决方案8】:

您可以通过加载/计算的操作系统在服务器上运行持久性脚本,甚至定期将 sql 数据重新加载/重新计算到某种内存结构中,然后通过套接字从其他脚本访问内存中的数据.

【讨论】:

    猜你喜欢
    • 2012-06-07
    • 1970-01-01
    • 2021-06-07
    • 2011-04-13
    • 2021-06-24
    • 1970-01-01
    • 2020-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多