The itertools
documentation has several recipes, among them a very neat round-robin recipe. I would also use ExitStack
to work with multiple file context-managers:
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)
Although, perhaps the best design is to use inversion of control, and provide the sequence of file-objects as an argument to .get
, so the calling code should take care of using an exit-stack:
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)