将文件对象子类化为 "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 仍然会做它应该做的事情。
我的想法是摆脱用户如何不断使用 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 仍然会做它应该做的事情。