List Comprehension语法错误中嵌套的if / else子句

时间:2018-03-29 14:22:36

标签: python python-3.x list-comprehension

从以下列表理解开始:

new_list = [re.sub(r'replace_a', 'with__b', i)
             if not re.findall(r'find_pattern', i, re.M) else 
            re.sub(r'replace_c', r'with__d', i)
            for i in old_list]

现在我想在else之后添加一个使用re.sub()替换另一个模式的if条件。我尝试过:

new_list = [re.sub(r'replace_a', 'with_b', i)
             if not re.findall(r'find_pattern', i, re.M) else
           (re.sub(r'replace_c', r'with_d', i)
             if foo == re.match("pattern", foo))
           for i in old_list]

试图找出使用列表推导的正确方法,但没有运气。

2 个答案:

答案 0 :(得分:5)

这将作为常规for循环更容易阅读:

new_list = []
for i in old_list:
    if not re.findall(r'find_pattern', i, re.M):
        new_list.append(re.sub(r'replace_a', 'with_b', i))
    elif foo == re.match("pattern", foo):
        new_list.append(re.sub(r'replace_c', r'with_d', i))
    # else:
    #    i

你的问题是条件表达式必须总是采用else子句,因为无论条件是否为真,它都必须有一些值。但是,使用if 语句,您可以省略else。例如,上述内容可以使new_list短于old_list,因为不是每个i都需要调用new_list.append。取消注释else会产生与jpp的答案相同的结果。

如果您坚持使用列表推导,那么您可以对其进行格式化以使其更具可读性。考虑

new_list = [re.sub(r'replace_pattern_a', 'with_pattern_b', i)
               if not re.findall(r'find_pattern', i, re.M) else 
            re.sub(r'replace_pattern_c', r'with_pattern_d', i) 
               if foo == re.match("pattern", foo) else
            i
            for i in old_list]

虽然条件表达式确实不是为这种嵌套而设计的。这在视觉上将可以添加到新列表中的表达式与用于做出决定的条件分开,但我不是一个大粉丝。还有其他格式可以做出不同的权衡取舍,但IMO常规的for循环是优越的。

正如jpp所提到的,重构的另一种方法是定义生成器函数:

def foo(old_list):
    for i in old_list:
        if not re.findall(r'find_pattern', i, re.M):
            yield re.sub(r'replace_a', 'with_b', i))
        elif foo == re.match("pattern", foo):
            yield re.sub(r'replace_c', r'with_d', i))
        else:
            yield i

new_list = list(foo())

也有其优点和缺点。 (我认为列举这些可能超出了这个答案的范围,但它确实介于单个列表理解和显式for循环的两个极端之间。它也最接近我错过Perl的构造天,do语句,类似于lambda,不带任何参数,但可以包含任意语句。)

答案 1 :(得分:2)

您需要在没有满足任何条件时指定else条件。例如:

import re

old_list = ['a', 'b', 'c']

new_list = [re.sub(r'replace_pattern_a', 'with_pattern_b', i) if not re.findall(r'find_pattern', i, re.M) \
            else re.sub(r'replace_pattern_c', r'with_pattern_d', i) if foo == re.match("pattern", foo) \
            else i for i in old_list]

# ['a', 'b', 'c']

然而,这是不可读的。我建议,如果性能不是问题,那么您可以创建一个函数来使用标准if / elif / else构造进行此操作。