Python:动态处理大型文档的行

Python:动态处理大型文档的行,python,readlines,Python,Readlines,我有一个文档看起来有点像这样: key1 value_1_1 value_1_2 value_1_3 etc key2 value_2_1 value_2_2 value_2_3 etc key3 value_3_1 value_3_2 value_3_3 etc etc 其中每个键是一个字符串,每个值是一个浮点数,都用空格分隔。每一行都有数百个与之关联的值,并且有数十万行。每一行都需要以特定的方式进行处理,但由于我的程序只需要一小部分行的信息,因此立即处理每一行似乎是巨大的时间浪费。目前,我

我有一个文档看起来有点像这样:

key1 value_1_1 value_1_2 value_1_3 etc
key2 value_2_1 value_2_2 value_2_3 etc
key3 value_3_1 value_3_2 value_3_3 etc
etc
其中每个
是一个字符串,每个
是一个浮点数,都用空格分隔。每一行都有数百个与之关联的值,并且有数十万行。每一行都需要以特定的方式进行处理,但由于我的程序只需要一小部分行的信息,因此立即处理每一行似乎是巨大的时间浪费。目前,我只有一个未处理行的列表,并维护一个包含每个
键的单独列表。当我需要访问一行时,我将使用
列表查找我需要的行的索引,然后在行列表中的该索引处处理该行。我的程序可能会要求多次查找同一行,这将导致重复处理同一行,但仍然比从一开始就处理每一行要好

我的问题是,有没有更有效的方法来做我正在做的事情

(如果我需要澄清,请告诉我)


谢谢

首先,我会将您的行存储在
目录中。这可能会使基于密钥的查找速度大大加快。制作这个dict可以像
d=dict(文件中的行的line.split(“”,1)
一样简单。例如,如果关键帧具有固定的宽度,则只需对线进行切片,就可以进一步加快速度

接下来,如果行处理的计算量很大,可以缓冲结果。我曾经通过将一个
dict
子类化来解决这个问题:

class BufferedDict(dict):
    def __init__(self, file_obj):
        self.file_dict = dict(line.split(' ', 1) for line in file_obj)

    def __getitem__(self, key):
        if key not in self:
            self[key] = process_line(self.file_dict[key])
        return super(BufferedDict, self).__getitem__(key)

def process_line(line):
    """Your computationally heavy line processing function"""

这样,如果调用
my\u buffered\u dict[key]
,则仅当处理的版本还不可用时,才会处理该行。

这里有一个类,它扫描文件并简单地缓存文件偏移量。仅当访问行的键时才处理行
\uu getitem\uu
缓存已处理的行

class DataFileDict:
    def __init__(self, datafile):
        self._index = {}
        self._file = datafile

        # build index of key-file offsets
        loc = self._file.tell()
        for line in self._file:
            key = line.split(None, 1)[0]
            self._index[key] = loc
            loc = self._file.tell()

    def __getitem__(self, key):
        retval = self._index[key]
        if isinstance(retval, int):
            self._file.seek(retval)
            line = self._file.readline()
            retval = self._index[key] = list(map(float, line.split()[1:]))
            print("read and return value for {} from file".format(key))
        else:
            print("returning cached value for {}".format(key))
        return retval

if __name__ == "__main__":
    from io import StringIO

    sample = StringIO("""\
A 1 2 3 4 5
B 6 7 8 9 10
C 5 6 7 8 1 2 3 4 5 6 7
""")

    reader = DataFileDict(sample))
    print(reader['A'])
    print(reader['B'])
    print(reader['A'])
    print(reader['C'])
    print(reader['D'])  # KeyError
印刷品

read and return value for A from file
[1.0, 2.0, 3.0, 4.0, 5.0]
read and return value for B from file
[6.0, 7.0, 8.0, 9.0, 10.0]
returning cached value for A
[1.0, 2.0, 3.0, 4.0, 5.0]
read and return value for C from file
[5.0, 6.0, 7.0, 8.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
Traceback (most recent call last):
  File "C:/Users/ptmcg/.PyCharm2017.1/config/scratches/scratch.py", line 64, in <module>
    print(reader['D'])  # KeyError
  File "C:/Users/ptmcg/.PyCharm2017.1/config/scratches/scratch.py", line 28, in __getitem__
    retval = self._index[key]
KeyError: 'D'
读取并返回来自文件的值
[1.0, 2.0, 3.0, 4.0, 5.0]
从文件中读取并返回B的值
[6.0, 7.0, 8.0, 9.0, 10.0]
返回缓存的值
[1.0, 2.0, 3.0, 4.0, 5.0]
从文件中读取并返回C的值
[5.0, 6.0, 7.0, 8.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
回溯(最近一次呼叫最后一次):
文件“C:/Users/ptmcg/.PyCharm2017.1/config/scratches/scratch.py”,第64行,在
打印(读卡器['D'])#键错误
文件“C:/Users/ptmcg/.PyCharm2017.1/config/scratches/scratch.py”,第28行,在u getitem中__
retval=自索引[键]
KeyError:'D'

您的dict理解将该行拆分两次,一次用于获取密钥,一次用于从该行中删除密钥以获取值,OP试图避免额外的工作。我想你可以用
self.file\u dict={parts[0]:parts[1:]for line in file\u obj for parts in[line.split()]}
来解决这个问题,但这太难看了,我可能只会使用显式for循环。另外,由于您的类扩展了dict,那么代码可能会调用
\uuuuuu setitem\uuuuuu
,这对这个应用程序来说并不太合适。关于
\uuuuuu setitem\uuuuu
,您的具体观点是什么?谢谢你对理解的建议。你必须去掉
line.split()
周围的方括号,才能让你的想法发挥作用,你还必须再次连接
parts[1:]
才能将行作为值返回。如果你想让其余的行仍然作为单个字符串连接,那么将其更改为
self.file\u dict={parts[0]:parts[1]表示文件中的行\u obj表示文件中的部分[line.split(None,1)]}
,这样您只需进行一次拆分。是的,您仍然需要方括号。但就这一点而言,使用dict构造函数本身而不是扭曲成dict理解可能更为简洁:
self.file\u dict=dict(line.split(None,1)表示文件中的行\u obj)
。同意,将dict构造函数与iterable一起使用将是最干净的。我会更新我的答案!但是我仍然不明白你关于
\uuuuuuu setitem\uuuuuuu
的观点。键的长度是固定的吗?还是只是一些非空格字符序列?这是一个不可预测的非空格字符序列,因此你不得不使用split()要获取它们,至少要执行
line.split(None,1)
,这样您只需完成获取前导键所需的最少工作。