Python - читать из нескольких больших файлов в paralell и выводить их по отдельности - PullRequest
1 голос
/ 07 февраля 2020

У меня есть несколько больших файлов, и мне нужно построчно выводить их в виде кругового списка. Примерно такой псевдокод:

    def get(self):
        with open(file_list, "r") as files:
            for file in files:
                yield file.readline()

Как бы я это сделал?

Ответы [ 2 ]

1 голос
/ 07 февраля 2020

Документация itertools имеет несколько рецептов, среди которых очень аккуратный рецепт с круговым приемом. Я бы также использовал ExitStack для работы с несколькими файловыми менеджерами контекста:

from itertools import cycle, islice
from contextlib import ExitStack

# https://docs.python.org/3.8/library/itertools.html#itertools-recipes
def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    num_active = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while num_active:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            # Remove the iterator we just exhausted from the cycle.
            num_active -= 1
            nexts = cycle(islice(nexts, num_active))

...

def get(self):
    with open(files_list) as fl:
        filenames = [x.strip() for x in fl]
    with ExitStack() as stack:
        files = [stack.enter_context(open(fname)) for fname in filenames]
        yield from roundrobin(*files)

Хотя, возможно, лучшим вариантом является использование инверсии управления и предоставление последовательности файлов -объекты в качестве аргумента .get, поэтому вызывающий код должен позаботиться об использовании стека выхода:

class Foo:
    ...
    def get(self, files):
        yield from roundrobin(*files)

# calling code:
foo = Foo() # or however it is initialized

with open(files_list) as fl:
    filenames = [x.strip() for x in fl]
with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    for line in foo.get(files):
        do_something_with_line(line)
0 голосов
/ 07 февраля 2020

Это было бы сложно (или потребовало бы некоторых дополнительных библиотек) сделать с помощью диспетчера контекста, но это не должно быть очень сложно без. open() принимает имя отдельного файла, поэтому, если file_list представляет собой список строк с именами входных файлов, это должно работать:

def get(files_list):
  file_handles = [open(f, 'r') for f in files_list]
  while file_handles:
    for fd in file_handles:
      line = fd.readline()
      if line:
        yield line
      else:
        file_handles.remove(fd)

Я предполагаю, что вы хотите продолжать до тех пор, пока каждый строка читается из каждого файла, более короткие файлы сбрасываются при достижении EOF.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...