从多个文件的行创建生成器
Create Generator From Lines of Multiple Files
我有一个主文件和一组子文件,但在查看主文件之前我不知道子文件的名称。
主文件包含两列:一些数据和第二个文件名,例如
data1_from_master hidden_file1
data2_from_master hidden_file2
data3_from_master hidden_file1
data4_from_master hidden_file3
data5_from_master hidden_file1
我想做的是创建一个生成器,它从主文件的第一列生成一个元素,然后从一个附属文件中生成一行数据。例如,
data1_from_master line1_from_file1
data2_from_master line1_from_file2
data3_from_master line2_from_file1
data4_from_master line1_from_file3
data5_from_master line3_from_file1
主文件的行数等于所有子文件的行数之和,所以主文件遍历完,所有的子文件也都遍历完了。
如果我只有两个文件想打开,并且我事先知道它们的名字,我可以做类似的事情。
with open(master_file, 'r') as a, open(hidden_file, 'r') as b:
for line1, line2 in zip(a, b):
yield (line1, line2)
但问题是,直到我读取主文件的给定行,我才知道要读取哪个子文件。然后尝试构建一个包含多个不同文件行的生成器会增加复杂性。
您可以保留 "cache" 个打开的文件并在需要时调用 fileobj.readline()
:
def read_master_file(master):
other_files = {}
for line in master:
data, name = line.split()
if name not in otherfiles:
other_files[name] = open(name)
yield data, other_files[name].readline()
for f in other_files.values():
f.close()
用作:
with open('master') as master:
for data, line in read_master_file(master):
# do stuff
不幸的是,这是您必须使用没有 with
的文件的情况之一,因为您不知道必须处理多少文件。
您可以 编写一个自定义上下文管理器来保存 "cache" 以实现类似:
def read_master_file(master):
with OtherFiles() as other_files:
for line in master:
data, name = line.split()
yield data, other_files.get_file(name).readline()
其中 get_file
将查找缓存并可能打开文件,OtherFiles()
的 __exit__
方法将关闭打开的文件。
但如果这是唯一使用它的地方,那它就没有意义了。
您想使用 ExitStack
。这是 contextlib
库提供的帮助程序 class,允许组合上下文管理器。它可用于在单个 with
语句中保持打开多个文件。
from contextlib import ExitStack
def iter_master_file(filename):
with ExitStack() as stack:
master = stack.enter_context(open(filename))
hidden_files = {}
for line in master:
# You can parse the lines as you like
# Here I just assume the last word is a file name
*data, file = line.split()
if file not in hidden_files:
hidden_files[file] = stack.enter_context(open(file))
yield ' '.join(data), next(hidden_files[file]).strip()
例子
让我们为此示例设置一些文件。
文件
master.txt
master says hidden1.txt is: hidden1.txt
master says hidden2.txt is: hidden2.txt
master says hidden1.txt is: hidden1.txt
master says hidden2.txt is: hidden2.txt
hidden1.txt
I am hidden file 1 line 1
I am hidden file 1 line 2
hidden2.txt
I am hidden file 2 line 1
I am hidden file 2 line 2
这是实际的例子。
代码
for data, hidden_data in iter_master_file('master.txt'):
print(data, hidden_data)
输出
master says hidden1.txt is: I am hidden file 1 line 1
master says hidden2.txt is: I am hidden file 2 line 1
master says hidden1.txt is: I am hidden file 1 line 2
master says hidden2.txt is: I am hidden file 2 line 2
我有一个主文件和一组子文件,但在查看主文件之前我不知道子文件的名称。
主文件包含两列:一些数据和第二个文件名,例如
data1_from_master hidden_file1
data2_from_master hidden_file2
data3_from_master hidden_file1
data4_from_master hidden_file3
data5_from_master hidden_file1
我想做的是创建一个生成器,它从主文件的第一列生成一个元素,然后从一个附属文件中生成一行数据。例如,
data1_from_master line1_from_file1
data2_from_master line1_from_file2
data3_from_master line2_from_file1
data4_from_master line1_from_file3
data5_from_master line3_from_file1
主文件的行数等于所有子文件的行数之和,所以主文件遍历完,所有的子文件也都遍历完了。
如果我只有两个文件想打开,并且我事先知道它们的名字,我可以做类似的事情。
with open(master_file, 'r') as a, open(hidden_file, 'r') as b:
for line1, line2 in zip(a, b):
yield (line1, line2)
但问题是,直到我读取主文件的给定行,我才知道要读取哪个子文件。然后尝试构建一个包含多个不同文件行的生成器会增加复杂性。
您可以保留 "cache" 个打开的文件并在需要时调用 fileobj.readline()
:
def read_master_file(master):
other_files = {}
for line in master:
data, name = line.split()
if name not in otherfiles:
other_files[name] = open(name)
yield data, other_files[name].readline()
for f in other_files.values():
f.close()
用作:
with open('master') as master:
for data, line in read_master_file(master):
# do stuff
不幸的是,这是您必须使用没有 with
的文件的情况之一,因为您不知道必须处理多少文件。
您可以 编写一个自定义上下文管理器来保存 "cache" 以实现类似:
def read_master_file(master):
with OtherFiles() as other_files:
for line in master:
data, name = line.split()
yield data, other_files.get_file(name).readline()
其中 get_file
将查找缓存并可能打开文件,OtherFiles()
的 __exit__
方法将关闭打开的文件。
但如果这是唯一使用它的地方,那它就没有意义了。
您想使用 ExitStack
。这是 contextlib
库提供的帮助程序 class,允许组合上下文管理器。它可用于在单个 with
语句中保持打开多个文件。
from contextlib import ExitStack
def iter_master_file(filename):
with ExitStack() as stack:
master = stack.enter_context(open(filename))
hidden_files = {}
for line in master:
# You can parse the lines as you like
# Here I just assume the last word is a file name
*data, file = line.split()
if file not in hidden_files:
hidden_files[file] = stack.enter_context(open(file))
yield ' '.join(data), next(hidden_files[file]).strip()
例子
让我们为此示例设置一些文件。
文件
master.txt
master says hidden1.txt is: hidden1.txt
master says hidden2.txt is: hidden2.txt
master says hidden1.txt is: hidden1.txt
master says hidden2.txt is: hidden2.txt
hidden1.txt
I am hidden file 1 line 1
I am hidden file 1 line 2
hidden2.txt
I am hidden file 2 line 1
I am hidden file 2 line 2
这是实际的例子。
代码
for data, hidden_data in iter_master_file('master.txt'):
print(data, hidden_data)
输出
master says hidden1.txt is: I am hidden file 1 line 1
master says hidden2.txt is: I am hidden file 2 line 1
master says hidden1.txt is: I am hidden file 1 line 2
master says hidden2.txt is: I am hidden file 2 line 2