Python中的嵌套块,嵌套层级可变
我想把几个不同的csv文件中的某些列合并成一个csv文件,并且加上新的标题,水平排列。我只想选择特定的列,这些列是根据标题来选的。每个要合并的文件中的列都不一样。
输入示例:
freestream.csv:
static pressure,static temperature,relative Mach number
1.01e5,288,5.00e-02
fan.csv:
static pressure,static temperature,mass flow
0.9e5,301,72.9
exhaust.csv:
static pressure,static temperature,mass flow
1.7e5,432,73.1
期望的输出:
combined.csv:
P_amb,M0,Ps_fan,W_fan,W_exh
1.01e5,5.00e-02,0.9e6,72.9,73.1
可能的函数调用:
reorder_multiple_CSVs(["freestream.csv","fan.csv","exhaust.csv"],
"combined.csv",["static pressure,relative Mach number",
"static pressure,mass flow","mass flow"],
"P_amb,M0,Ps_fan,W_fan,W_exh")
这里是之前的代码版本,只允许一个输入文件。我是参考了在Python中以不同顺序写入CSV列来写的:
def reorder_CSV(infilename,outfilename,oldheadings,newheadings):
with open(infilename) as infile:
with open(outfilename,'w') as outfile:
reader = csv.reader(infile)
writer = csv.writer(outfile)
readnames = reader.next()
name2index = dict((name,index) for index, name in enumerate(readnames))
writeindices = [name2index[name] for name in oldheadings.split(",")]
reorderfunc = operator.itemgetter(*writeindices)
writer.writerow(newheadings.split(","))
for row in reader:
towrite = reorderfunc(row)
if isinstance(towrite,str):
writer.writerow([towrite])
else:
writer.writerow(towrite)
为了把这个代码改成可以处理多个文件,我发现需要做以下几点:
-我需要把输入文件名、旧标题和新标题都变成一个列表(长度要一样)
-我需要遍历输入文件的列表,来创建一个读取器的列表
-读取的名称也可以是一个列表,遍历这些读取器
-这意味着我可以把name2index做成一个字典的列表
我不知道怎么做的是,使用关键字with
,在运行时深度嵌套n层,而n的值只有在运行时才能知道。我看过这个:如何在Python中使用“with open”打开多个文件?,但这似乎只适用于你知道需要打开多少个文件的情况。
或者有没有更好的方法来做到这一点?
我对Python还很陌生,所以非常感谢你能给我的任何建议。
2 个回答
0
我不太确定这样做是否正确,但我想对Bas Swinckels的回答做一些补充。他的回答非常有帮助,但有几个小地方不太一致,所以我想提供正确的代码。
这是我做的事情,结果是成功的。
from contextlib import contextmanager
import csv
import operator
import itertools as IT
@contextmanager
def open_many_files(filenames):
files=[open(filename,'r') for filename in filenames]
try:
yield files
finally:
for f in files:
f.close()
def reorder_multiple_CSV(infilenames,outfilename,oldheadings,newheadings):
with open_many_files(filter(None,infilenames.split(','))) as handles:
with open(outfilename,'w') as outfile:
readers=[csv.reader(f) for f in handles]
writer = csv.writer(outfile)
reorderfunc=[]
for i, reader in enumerate(readers):
readnames = reader.next()
name2index = dict((name,index) for index, name in enumerate(readnames))
writeindices = [name2index[name] for name in filter(None,oldheadings[i].split(","))]
reorderfunc.append(operator.itemgetter(*writeindices))
writer.writerow(filter(None,newheadings.split(",")))
for rows in IT.izip_longest(*readers,fillvalue=['']*2):
towrite=[]
for i, row in enumerate(rows):
towrite.extend(reorderfunc[i](row))
if isinstance(towrite,str):
writer.writerow([towrite])
else:
writer.writerow(towrite)
2
我只想说一下关于用 with
同时打开多个文件的部分,前提是文件的数量事先不知道。其实写一个自己的 上下文管理器 并不难,可以像这样做(这个代码完全没有测试过):
from contextlib import contextmanager
@contextmanager
def open_many_files(filenames):
files = [open(filename) for filename in filenames]
try:
yield files
finally:
for f in files:
f.close()
你可以这样使用它:
innames = ['file1.csv', 'file2.csv', 'file3.csv']
outname = 'out.csv'
with open_many(innames) as infiles, open(outname, 'w') as outfile:
for infile in infiles:
do_stuff(in_file)
还有一个函数 可以做类似的事情,不过这个函数已经不推荐使用了。