如何"链"迭代器?

时间:2017-08-23 22:54:19

标签: python iterator

我试图将迭代器连同一个迭代器连同一个主文件和另一个迭代器一起读取主文件的每一行,并根据第一行的输出处理另一个文件。

我的工作代码如下

class MasterReader(object):

    def __init__(self, filename):
        self.f = open(filename, "r")

    def __iter__(self):
        return self

    def __next__(self):
        line = self.f.readline().strip()
        if line == "":
            raise StopIteration
        return line


class SubReader(object):

    def __init__(self, mr):
        self.mr = mr

    def __iter__(self):
        self._next()
        return self

    def _next(self):
        self.current = open(self.mr.__next__(), "r")

    def __next__(self):
        while True:
            line = self.current.readline().strip()
            if line == "":
                self._next()
                continue
            return line


mr = MasterReader("master")
sr = SubReader(mr)

for line in sr:
    print(line)

其中master是包含其他文件行的文件

file1
file2

file1包含

1.a
1.b
1.c

file2包含

2.a
2.b
2.c

输出

1.a
1.b
1.c
2.a
2.b
2.c

我的工作再次起作用,但感觉错误,我在while中有一个__next__循环我必须手动检查每个子文件的结尾并明确调用下一个master文件中的行。

有更好/更pythonic的方式吗?

编辑: 这是我试图完成的一个简化问题。在真实版本SubReader将是线程化的,我只需要一个MasterReader。实际上,这对我的线程项目不起作用,但是我想确保在深入陷入混乱之前对迭代器进行推广。

2 个答案:

答案 0 :(得分:2)

你可以在小函数的帮助下使用itertools.chain.from_iterable,从每个文件中产生剥离的行。

from itertools import chain

def fgen(fname):
    with open(fname) as f:
        for line in f:
            yield line.strip()

for a in chain.from_iterable(fgen(line) for line in fgen('master.txt')):
    print(a)

答案 1 :(得分:1)

由于文件对象本身就是一个迭代器,因此在两种情况下都不一定需要实现__next__,只需在__iter__ {<1>}中实现 yield 行。 }。更重要的是,使用 for 循环读取文件会隐式处理EOF:

class MasterReader(object):
    def __init__(self, filename):
        self.f = open(filename)

    def __iter__(self):
        for line in self.f:
            yield line.strip()
        self.f.close()


class SubReader(object):
    def __init__(self, mr):
        self.mr = mr

    def __iter__(self):
        for filename in mr:
            with open(filename) as f:
                for line in f:
                     yield line.strip()