【问题标题】:(locally) cached file object in PythonPython中的(本地)缓存文件对象
【发布时间】:2012-10-07 10:16:25
【问题描述】:

我经常处理需要通过网络访问的大型数据文件(主要通过 NFS,但有时也通过 CIFS)。出于性能原因,最好将这些文件缓存在本地硬盘上以最大程度地减少网络使用。

所以基本上我正在寻找一个自动处理本地缓存的文件对象,类似于以下内容:

import CachedFileObject as cfo
cfo.set_local_cache_dir("/tmp")
handle = cfo.open("/nfs/server1/bigdatafile.nc", "r") # copy file to /tmp, open the copy
# do stuff with the filehandle
del handle                                            # delete the local copy

我真的只需要这个来阅读文件。如果应该有一种简单的方法来获取/实现文件创建(甚至写入),那将是一个奖励。

非常感谢任何想法

【问题讨论】:

  • 第一次打开时复制整个文件的开销您是否满意?
  • 如果有办法绕过该开销,那将是最棒的 -- 但是,如果这不可能/太难实现,我可以忍受开销
  • 我确定您已经考虑过这一点,但只是将其放入其中,对于文件更新/创建,您需要在某个时候将更改同步回原始文件。要允许并发访问,您可能还需要考虑可能存在多个写入实例的竞争条件。
  • 当然,写作会很困难。基本上,我只需要阅读,如问题中所述。我编辑以强调这一点。

标签: python file caching file-io


【解决方案1】:

我会使用操作系统来缓存文件。 NFS 挂载可以使用 -o fsc 设置为缓存,并且 SMB 挂载默认情况下已经启用了一些缓存。

【讨论】:

  • 是的,那将是理想的。但是,它需要对我正在使用的机器的 root 访问权限,而我没有。
【解决方案2】:

有一个简单的解决方案(打开完整副本以进行读取访问,关闭完整副本以进行写入访问):

import os
import shutil

from tempfile import mkstemp

class CachedFileObject(object):

    def __init__(self, cache_dir="/tmp"):
        self.cache_dir = cache_dir
        self.local_file = None
        self.local_path = None
        self.remote_path = None
        self.mode = None

    def open(self, path, mode="r", buffering=-1):
        if self.local_file and not self.local_file.closed:
            raise ValueError("Already open")
        fd, self.local_path = mkstemp(dir=self.cache_dir)
        os.close(fd)
        try:
            if "r" in mode and not os.path.exists(path):
                raise ValueError("No such remote file")
            if os.path.exists(path):
                # have remote file
                self._cache_remote(path, self.local_path)
            self.local_file = open(self.local_path, mode=mode, buffering=buffering)
            self.mode = mode
            self.remote_path = path
        except Exception as e:
            os.unlink(self.local_path)
            raise

        return self

    def close(self):
        self.local_file.close()
        try:
            if set("wa+").intersection(set(self.mode)):
                # have writes, sync file back to remote side
                self._sync_remote(self.remote_path, self.local_path)
        finally:
            os.unlink(self.local_path)

    def _cache_remote(self, remote_path, local_path):
        # simple cp
        shutil.copy(remote_path, local_path)

    def _sync_remote(self, remote_path, local_path):
        shutil.copy(local_path, remote_path)

    def __getattr__(self, attr):
        if self.local_file is not None:
            return getattr(self.local_file, attr)
        else:
            raise ValueError("File is not opened")

创建的对象将作为常规文件运行,并在打开/关闭时复制/同步。

用法:

f = CachedFileObject(cache_dir="/your/tmp/dir")
f.open("/path/to/remote/file")
# ..your f.read()'s here..
f.close()

【讨论】:

  • 我可能很笨,但我不明白这将如何工作,因为您没有扩展 file 类。
  • 魔术隐藏在 __getattr__ 方法中 - 当您访问对象的属性但未找到它时(即此类中的 seek),将使用此属性名称作为字符串调用 __getattr__。然后,我的实现检查真实文件对象是否存在并从该文件对象返回属性。调用顺序如下所示:f.seek(42) -> f.__getattr__("seek") -> getattr(self.local_file, "seek") 等价于 self.local_file.seek -> __getattr__ 返回 self.local_file.seek -> self.local_file.seek(42)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-14
  • 1970-01-01
  • 1970-01-01
  • 2017-06-23
  • 1970-01-01
  • 2020-05-02
  • 1970-01-01
相关资源
最近更新 更多