我有以下两个函数从csv文件中提取数据,一个返回一个列表,另一个返回一个生成器:
列表:
def data_extraction(filename,start_line,node_num,span_start,span_end):
with open(filename, "r") as myfile:
file_= csv.reader(myfile, delimiter=',') #extracts data from .txt as lines
return [filter(lambda a: a != '', row[span_start:span_end]) \
for row in itertools.islice(file_, start_line, node_num+1)]
发电机:
def data_extraction(filename,start_line,node_num,span_start,span_end):
with open(filename, "r") as myfile:
file_= csv.reader(myfile, delimiter=',') #extracts data from .txt as lines
return (itertools.ifilter(lambda a: a != '', row[span_start:span_end]) \
for row in itertools.islice(file_, start_line, node_num+1))
我通过调用以下函数之一来启动程序以提取数据。
下一行是:print [x in data]
当我使用返回列表的函数时,一切正常,当我使用我得到的生成器时:ValueError: I/O operation on closed file
我从其他问题中收集到这是因为with open
函数data_extraction
可能导致returns
语句丢失。
问题是:是否有一种解决方法可以保留一个独立的函数来提取数据,这样我就不必将所有代码都放在一个函数中?其次,我能够重置发生器多次使用它吗?
希望将生成器保留在列表中的原因是我正在处理大型数据集。
答案 0 :(得分:2)
请注意,with
语句会在文件末尾关闭文件。这意味着无法再从中读取数据。
列表版本实际读入所有数据,因为必须创建列表元素。
在您实际从生成器获取数据之前,生成器版本决不会读取任何数据。由于您在关闭文件后执行此操作,因此生成器将失败,因为它然后尝试获取它。
您只能通过实际读取数据来避免这种情况,例如:正如您通过创建列表所做的那样。试图不保留所有数据(生成器)但仍希望拥有所有数据(关闭文件)没有意义。
另一种方法是每次打开文件进行读取 - 文件对象就像其值的生成器一样。如果要避免重复过滤代码,可以为此创建一个包装器:
直接的方法是将发电机返回功能转换为发电机功能本身:
def data_extraction(filename,start_line,node_num,span_start,span_end):
with open(filename, "r") as myfile:
file_= csv.reader(myfile, delimiter=',') #extracts data from .txt as lines
for item in (itertools.ifilter(lambda a: a != '', row[span_start:span_end]) \
for row in itertools.islice(file_, start_line, node_num+1)):
yield item
这有一个问题:with
语句只会在生成器耗尽或收集后关闭。这会带来与打开文件相同的情况,您也必须完成此操作。
更安全的替代方案是使用过滤器生成器并将其提供给文件内容:
def data_extraction(file_iter, start_line, node_num, span_start, span_end):
file_= csv.reader(file_iter, delimiter=',') #extracts data from .txt as lines
for item in (itertools.ifilter(lambda a: a != '', row[span_start:span_end]) \
for row in itertools.islice(file_, start_line, node_num+1)):
yield item
# use it as such:
with with open(filename, "r") as myfile:
for line in data_extraction(mayflies):
# do stuff
如果您经常需要这个,您还可以通过实现上下文管理器协议来创建自己的类。然后,可以在with
语句中使用此语句,而不是open
。
class FileTrimmer(object):
def __init__(self, filename, start_line, node_num, span_start, span_end):
# store all attributes on self
def __enter__(self):
self._file = open(self.filename, "r")
csv_reader = csv.reader(self._file, delimiter=',') #extracts data from .txt as lines
return (
itertools.ifilter(
lambda a: a != '',
row[self.span_start:self.span_end])
for row in itertools.islice(
csv_reader,
self.start_line,
self.node_num+1
))
def __exit__(self, *args, **kwargs):
self._file.close()
您现在可以像这样使用它:
with FileTrimmer('/my/file/location.csv', 3, 200, 5, 10) as csv_rows:
for row in csv_rows: # row is an *iterator* over the row
print('<', '>, <'.join(row), '>')