如何编写此 Python 代码以使用 2+ 个更少的嵌套 if 语句?

How do I write this Python code to use 2+ fewer nested if statements?

我有以下代码,我用它循环遍历 parquet 元数据文件中的行组,以查找整个文件中 i、j、k 列的最大值。据我所知,我必须在每个行组中找到最大值。

我正在寻找:

我尝试使用字典 lambda 组合作为 switch 语句来代替一些 if 语句,并消除至少两层嵌套,但我不知道如何在没有进一步嵌套。

import pyarrow.parquet as pq


def main():
    metafile = r'D:\my_parquet_meta_file.metadata'
    meta = pq.read_metadata(metafile)

    max_i = 0
    max_j = 0
    max_k = 0

    for grp in range(0, meta.num_row_groups):
        for col in range(0, meta.num_columns):
            # locate columns i,j,k
            if meta.row_group(grp).column(col).path_in_schema in ['i', 'j', 'k']:
                if meta.row_group(grp).column(col).path_in_schema == 'i':
                    if meta.row_group(grp).column(col).statistics.max > max_i:
                        max_i = meta.row_group(grp).column(col).statistics.max
                if meta.row_group(grp).column(col).path_in_schema == 'j':
                    if meta.row_group(grp).column(col).statistics.max > max_j:
                        max_j = meta.row_group(grp).column(col).statistics.max
                if meta.row_group(grp).column(col).path_in_schema == 'k':
                    if meta.row_group(grp).column(col).statistics.max > max_k:
                        max_k = meta.row_group(grp).column(col).statistics.max

    print('max i: ' + str(max_i), 'max j: ' + str(max_j), 'max k: ' + str(max_k))


if __name__ == '__main__':
    main()

有人给我 2 个解决方案:

第一个涉及使用列表来保存我指定的每个列的最大值,然后使用 python max 函数评估较高的值,然后再将其分配回去。我必须说我不太喜欢使用未命名的位置最大值变量,但在这种情况下它可以完成工作,我不能错。

解决方案一:

import pyarrow.parquet as pq

def main():
    metafile = r'D:\my_parquet_meta_file.metadata'
    meta = pq.read_metadata(metafile)
    max_value = [0, 0, 0]
    for grp in range(0, meta.num_row_groups):
        for col in range(0, meta.num_columns):
            column = meta.row_group(grp).column(col)
            for i, name in enumerate(['i', 'j', 'k']):
                if column.path_in_schema == name:
                    max_value[i] = max(max_value[i], column.statistics.max)

    print(dict(zip(['max i', 'max j', 'max k'], max_value)))

if __name__ == '__main__':
    main()

第二个使用类似的方法,但在遍历每个列对象以查找列的最大值之前,还使用列表推导来获取所有列对象。这消除了一个额外的嵌套级别,但更重要的是在询问它们之前将列对象的收集分离到一个单独的集合中,使过程更清晰一些。我认为不利的一面是可能需要更高的内存使用量,因为列对象中的所有内容都被保留而不仅仅是报告的最大值。

:

方案二:

import pyarrow.parquet as pq

def main():
    metafile = r'D:\my_parquet_meta_file.metadata'
    meta = pq.read_metadata(metafile)
    max_value = [0, 0, 0]
    columns = [meta.row_group(grp).column(col)
               for col in range(0, meta.num_columns)
               for grp in range(0, meta.num_row_groups)] # Apparently list generators are read right to left
    for column in columns:
        for i, name in enumerate(['i', 'j', 'k']):
            if column.path_in_schema == name:
                max_value[i] = max(max_value[i], column.statistics.max)
    print(dict(zip(['max i', 'max j', 'max k'], max_value)))

    if __name__ == '__main__':
    main()

*更新我发现它实际上使用更少的内存——我提到的列对象实际上是一个列表生成器而不是列表。在我通过“列”列表生成器枚举的第二个循环中调用它之前,它不会检索每一列。使用列表生成器的缺点是您只能迭代一次(它不可重用),除非您重新定义它。 好的一面是,如果我碰巧想在找到所需值后从循环中“中断”,我可以并且不会有剩余列表占用内存并且它不需要调用每个列对象使其更快.就我而言,这并不重要,因为无论如何我都会浏览整个列表,但内存占用较少。

*注意这里的列表生成器是一个 Python 3 功能,Python 2 会返回相同语法的完整列表

# In Python 3 this returns a list generator, in Python 2 it returns a populated lsit
columns = [meta.row_group(grp).column(col)
               for col in range(0, meta.num_columns)
               for grp in range(0, meta.num_row_groups)]

要像在 Python 2 中那样获取填充列表,需要 list() 函数 例如 columns = list([<generator expression ... >])

您可以使用以下函数模拟 switch 语句:

def switch(v):yield lambda *c:v in c

它使用单遍 for 循环模拟 switch 语句,其中 if/elif/else 条件不重复切换值:

例如:

for case in switch(x):
    if    case(3):     
          # ... do something
    elif  case(4,5,6): 
          # ... do something else
    else:              
          # ... do some other thing

它也可以用在更C的风格中:

for case in switch(x):

    if case(3):     
       # ... do something
       break

    if case(4,5,6): 
       # ... do something else
       break 
else:              
    # ... do some other thing

以下是如何在您的代码中使用它:

...
for case in switch(meta.row_group(grp).column(col).path_in_schema):
    if not case('i', 'j', 'k'): break
    statMax = meta.row_group(grp).column(col).statistics.max
    if   case('i') and statMax > max_i: max_i = statMax
    elif case('j') and statMax > max_j: max_j = statMax
    elif case('k') and statMax > max_k: max_k = statMax
...