【问题标题】:Can I treat a file as a list in python?我可以将文件视为python中的列表吗?
【发布时间】:2019-04-10 00:37:33
【问题描述】:

这是一个问题,但我也只是希望我不必编写一堆代码来获得我想要的行为。 (另外,如果它已经存在,它的运行速度可能比我写的要快。)我有许多无法放入内存的大型数字列表——至少不是同时存在的。这很好,因为我一次只需要每个列表的一小部分,而且我知道如何将列表保存到文件中并读出我需要的列表部分。问题是我这样做的方法有些低效,因为它涉及遍历文件以获得我想要的部分。所以,我想知道是否碰巧有一些库或我没有找到的东西允许我使用我熟悉的[] 表示法来索引文件,就好像它是一个列表一样。由于我自己编写文件,因此我可以根据需要对它们进行格式化,但目前我的文件只包含列表中的元素,\n 作为值之间的分隔符。

只是为了回顾一下我在寻找什么/让它更具体。

  1. 我想使用列表索引表示法(包括切片为子列表和负索引)来访问写入文件中的列表的内容
  2. 被访问的子列表(例如f[1:3])应该在内存中作为python列表对象返回
  3. 我希望能够分配给文件的索引(例如f[i] = x 应该将值x 写入文件f 对应于索引i 的位置)

老实说,我不希望这种情况存在,但你永远不知道什么时候会错过研究中的某些内容。所以,我想我会问。附带说明,如果这不存在,是否可以在 python 中重载[] 运算符?

【问题讨论】:

  • 您可以通过定义__getitem__ 来重载[] 运算符。另外,当您说“索引”时,您的意思是“文件中的位置”(而不是行号),对吧?
  • 作为高效的最低要求,您需要编写具有固定帧长度的记录,以允许利用seek 定位您想要的项目,而无需考虑可变的行长度
  • @gmds 通过索引,我的意思是存储在文件中的列表中的位置。目前对我来说意味着行号,因为这是分隔列表元素的原因,但我目前在想我想做 donkopotamus 建议的固定帧长度(这意味着我应该切换到二进制文件?),但我对“文件中的位置”是我所在的精确字符/字节,这不是我想要的,因为每个帧都跨越多个字节。我想我想做的是使用seekstruct 使用c 类型格式读取/写入文件,但我需要做更多的研究才能确定。

标签: python list file


【解决方案1】:

如果您的数据是纯数字的,您可以考虑使用numpy 数组,并以npy 格式存储数据。以这种格式存储后,您可以将内存映射文件加载为:

>>> X = np.load("some-file.npy", mmap_mode="r")
>>> X[1000:1003]
memmap([4, 5, 6])

此访问将直接从磁盘加载,无需加载前导数据。

【讨论】:

  • 如果 OP 无法将所有数据放入内存中,有没有办法首先写入这个文件?
  • 当然......例如使用numpy.memmap
  • 我认为将其包含在您的解决方案中可能会有所帮助,这是我的观点。
  • 这看起来可能是我需要的,但是查看 memmap 的文档让我想起了 python mmap 模块。现在我不确定我需要哪一个,因为它们似乎在很大程度上做同样的事情 memmap 只是特定于numpy。你有什么理由特别建议 ​​memmap 而不是 mmap?
  • 只是因为,为了快速访问,您需要一个框架协议,该协议将使用固定长度的记录来存储元素(这意味着实现可以快速找到正确的位置)......如果你只是mmap您的文本文件将没有具有固定长度的记录,并且无法优化寻找正确的索引...有很多其他替代方案,例如hdf5跨度>
【解决方案2】:

我认为你实际上可以通过编写一个简单的类来做到这一点:

class FileWrapper:

    def __init__(self, path, **kwargs):
        self._file = open(path, 'r+', **kwargs)

    def _do_single(self, where, s=None):
        if where >= 0:
            self._seek(where)

        else:
            self._seek(where, 2)

        if s is None:
            return self._read(1)

        else:
            return self._write(s)

    def _do_slice_contiguous(self, start, end, s=None):
        if start is None:
            start = 0

        if end is None:
            end = -1

        self._seek(start)
        if s is None:
            return self._read(end - start)

        else:
            return self._write(s)

    def _do_slice(self, where, s=None):
        if s is None:
            result = []
            for index in where:
                file._seek(index)
                result.append(file.read(1))

            return result

        else:
            for index, char in zip(where, s):
                file._seek(index)
                file._write(char)

            return len(s)

    def __getitem__(self, key):
        if isinstance(key, int):
            return self._do_single(key)

        elif isinstance(key, slice):
            if self._is_contiguous(key):
                return self._do_slice_contiguous(key.start, key.stop)

            else:
                return self._do_slice(self._process_slice(key))

        else:
            raise ValueError('File indices must be ints or slices.')

    def __setitem__(self, key, value):
        if isinstance(key, int):
            return self._do_single(key, value)

        elif isinstance(key, slice):
            if self._is_contiguous(key):
                return self._do_slice_contiguous(key.start, key.stop, value)

            else:
                where = self._process_slice(key)
                if len(where) == len(value):
                    return self._do_slice(where, value)

                else:
                    raise ValueError('Length of slice not equal to length of string to be written.')


    def __del__(self):
        self._file.close()

    def _is_contiguous(self, key):
        return key.step is None or key.step == 1

    def _process_slice(self, key):
        return range(key.start, key.stop, key.step)

    def _read(self, size):
        return self._file.read(size)

    def _seek(self, offset, whence=0):
        return self._file.seek(offset, whence)

    def _write(self, s):
        return self._file.write(s)

我确信可以进行许多优化,因为我匆匆完成了这个,但写起来很有趣。

这并不能完全回答问题,因为它支持 字符 的随机访问,就像线一样,它们处于更高的抽象级别并且处理起来更复杂(因为它们可以可变长度)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-28
    • 2011-03-29
    • 2019-10-31
    • 2021-11-28
    • 2021-06-24
    • 1970-01-01
    相关资源
    最近更新 更多