【问题标题】:Nested if / else clauses in List Comprehension Syntax Error列表理解语法错误中的嵌套 if / else 子句
【发布时间】:2018-09-08 13:24:29
【问题描述】:

从以下列表理解开始:

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 之后添加一个if 条件,它使用re.sub() 替换另一个模式。我试过了:

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]

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

【问题讨论】:

  • 我认为最后的if 子句需要放在最后,因为它没有关联的else。你可以写出你知道的for 循环,或者至少添加一些换行符以使其可读
  • 您遇到的错误是什么?我认为您可能只需要添加一个最终的else,因为它看起来像这种模式而不是stackoverflow.com/a/9987533/1011724。或者,也许尝试删除您最后一个 else...if 子句周围的括号。但是foo == re.match("pattern", foo) 也不依赖于列表理解中的任何内容,所以您是否试图使整个子句以外部事物为条件?编辑您的 Q 以添加换行符,解释您想要的逻辑并删除正则表达式并添加 MCVE
  • 问题不在于列表理解;这是缺少其else 的条件表达式。
  • @Chris_Rands 道歉,添加了换行符。我尝试使用 for 循环,但在将元素附加到新列表时遇到问题。
  • @Dan 是的,正如你所说,它错过了决赛。对于foo = re.match,我想根据变量的值添加一个附加条件

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


【解决方案1】:

这将更容易作为常规 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_listold_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,不带任何参数,但可以包含任意语句。)

【讨论】:

  • 非常感谢您的详细解释以及使用for 循环的解决方案。我没有想过将re.sub 放在new.list(append) 中。有很多东西要学。 ~faber
  • 再看一遍,我可能还是会使用一个临时变量。 x = re.sub(...); new_list.append(x)。这还有一个好处是可以让您推迟对new_list.append 的调用,直到if 语句之后。只需在每个分支中适当设置x,然后在最后调用new_list.append(x)
  • 在我看来,更易读的选项是使用生成器函数和yield 项目。然后在需要时致电list。怀疑这里有任何性能改进,但这删除了实例化列表和使用 list.append 的样板代码。
  • chepner 和 jpp,再次感谢您。我一定会采纳您的建议以改进代码。
  • @chepner 感谢您展示带有生成器功能的解决方案。非常有趣,而且看起来很整洁。
【解决方案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 构造提供此操作。

【讨论】:

  • 至少,一个显式调用new_list.append 的常规for 循环就可以了。
  • @jpp,谢谢。我一直在努力修复它,现在我知道它是多么容易。
  • @chepner 我尝试了 for loop 和 new_list.append 但它没有保存列表中的元素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多