将文件对象子类化为 "fake" 作为可迭代对象 - Python

Subclassing a file object to "fake" it as an iterable - Python

我的想法是摆脱用户如何不断使用 seek(0) 来重置文本文件阅读。

所以我尝试创建一个 MyReader,它是一个 collections.Iterator,然后使用 .reset() 替换 seek(0),然后它从上次产生的地方继续通过保留 self.iterable 对象。

class MyReader(collections.Iterator):
    def __init__(self, filename):
        self.filename = filename
        self.iterable = self.__iterate__()

    def __iterate__(self):
        with open(self.filename) as fin:
            for line in fin:
                yield line.strip()

    def __iter__(self):
        for line in self.iterable:
            yield line

    def __next__(self):
        return next(self.iterable)

    def reset(self): 
        self.iterable = self.__iterate__()

用法类似于:

$ cat english.txt
abc
def
ghi
jkl

$ python

>>> data = MyReader('english.txt')
>>> print(next(data))
abc
>>> print(next(data))
def
>>> data.reset()
>>> print(next(data))
abc

我的问题是 这是否已经存在于 Python-verse 的某个地方? Esp。如果已经有一个本地对象做这样的事情,我想避免重新发明轮子 =)

如果不存在呢? 这个对象看起来有点不符合 Python 风格吗? 因为它说它是一个迭代器,但真正的迭代器实际上是 self.iterable,其他函数环绕它来做 "resets".

我认为这取决于您的实际情况。比方说,如果你只是想摆脱 file.seek(0),它可以很简单:

class MyReader:
    def __init__(self, filename, mode="r"):
        self.file = open(filename, mode)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()

    def __iter__(self):
        self.file.seek(0)
        for line in self.file:
            yield line.strip()

    def close(self):
        self.file.close()

您甚至可以像普通上下文管理器一样使用它:

with MyReader("a.txt") as a:
    for line in a:
        print(line)
    for line in a:
        print(line)

输出:

sdfas
asdf
asd
fas
df
asd
f
sdfas
asdf
asd
fas
df
asd
f

我对你的 MyReader class 有几点批评。我 去 post 一个 context manager 的替代方案,但 Sraw 先于我。 ;)

您不应使用以双下划线开头和结尾的名称,例如 __iterate__。这些名称本质上是为语言实现者保留的,如果将官方 __iterate__ 魔术方法添加到语言中,您的代码将会中断。如果你想要一个私有方法,你可以将它命名为 _iterate

那个 __iterate__ 方法有一个小问题:它的 with 块只有在当前 self.iterable 的文件被完全读取时才会退出,所以如果 MyReader 实例被重置然后你有一个旧的打开文件坐在周围,使用文件描述符。当然,它最终会在程序退出时关闭(或者您删除 MyReader 实例),但恕我直言,它很乱。

另外,我对 yield line.strip() 不是很满意。当然,当您阅读文本文件时,大多数时候这样做很方便,但在某些情况下,调用者可能希望查看任何前导或尾随的白色 space,而您已经取消了该选项。

顺便说一句,那个 __iter__ 方法是多余的:如果你删除那个方法,你的 class 仍然会做它应该做的事情。