Python中的嵌套块,嵌套层级可变

2 投票
2 回答
1484 浏览
提问于 2025-04-18 11:44

我想把几个不同的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)

还有一个函数 可以做类似的事情,不过这个函数已经不推荐使用了。

撰写回答