在 python 列表理解中添加额外的语句

Adding extra statements in a python list comprehension

我需要在包含特定字符串的文本文件中找到一行,然后将该行及其后面的所有行追加到列表中。这就是我完成它的方式..

file1 = open("input.txt", "r")
always_print = False
lines = file1.readlines()
output = []
for line in lines:
    if always_print or "def set" in line:  #def set is the string i want
        output.append(line)
        always_print = True

虽然这很好用,但我尝试使用列表 comprehensions.This 做同样的事情:

lines = [ item.strip() for item in open("input.txt")]
always_print = False
output = [item for item in lines if "def set" or print_always in item]

这显然不起作用,因为当所需字符串为 found.How 时我没有设置 always_print=True 我是否在列表理解范围内这样做?

编辑 更正我之前完全错误的答案:

并不是说我会在生产代码中使用它,而是通过破解它,你可以在列表理解中做到这一点...

always_print = []
output = [item for item in lines 
          if always_print
          or (always_print.append(1) if ("def set" in item) else None) 
          or "def set" in item]

只是为了解释一下,三个条件中的第二个总是 returns None,这是一个 Falsy 值。

进一步深入黑暗面,如果你不想评估 "def set" in item 两次:

always_print = []
output = [item for item in lines 
          if always_print
          or (
              (always_print.append(1) or True) if ("def set" in item) else False
             )]

编辑2

如果我更详细地描述这里发生的事情...

上面的代码(以及 Kevin 的代码,在他的评论中提到)使用了三个不同的技巧。

  1. 在 Python 中,大多数对象和值都有关联的布尔值。例如0 是 False,任何其他数字都是 True。同样,空列表是 False 而非空列表被评估为 True.
  2. 虽然像 a=1 这样的变量赋值没有 return 任何值并且不能作为列表理解的一部分包含在内,但 a_list.append(x) 是一个函数调用并且 returns None 计算为 False。它还具有将新元素 x 添加到 a_list.
  3. 的副作用
  4. andor 等逻辑运算符的计算顺序是从左到右。 and在第一个False值处停止执行,or在第一个True值处停止执行,可用于控制是否根据某些条件执行列表追加状况。三元运算符 'x if y else z' 也有求值顺序,但首先求值 'y',然后求值 'x' 或 'z',但不会同时求值。

如您所见,一组非常迂回的逻辑技巧可能在超优化的 C(或 IOCCC)中占有一席之地,但在 Python 中却没有。 可能 在列表理解中复制您的控制流,但实际上,每次都使用 dropwhile

使用 itertools.dropwhile() 查找包含您的 def set:

的第一行
from itertools import dropwhile

output = list(dropwhile(lambda l: 'def set' not in l, lines))

dropwhile() 将跳过 lines 中与您的测试不匹配的任何条目;一旦匹配,它就会停止测试并从那里简单地产生所有东西。

dropwhile() returns 一个迭代器;我在这里使用 list() 将其转换为行列表,但您也可以将其用作另一个循环的基础,例如剥离换行符等。

演示:

>>> from itertools import dropwhile
>>> lines = '''\
... foo
... bar
... def set():
...     spam
...     ham
...     eggs
... '''.splitlines(True)
>>> list(dropwhile(lambda l: 'def set' not in l, lines))
['def set():\n', '    spam\n', '    ham\n', '    eggs\n']

暂停可能是最好的解决方案。但是,如果您想要真正花哨的东西,请查看以下内容:

使用列表推导,您可以使用简单的检查和转换,但您应该看到列表推导更像是数学映射。但是,您可以为用于测试某个项目是否应包含某种状态的函数提供某种状态,即让您的检查记住是否已经发生了某些事情。使它成为一个仿函数(或函数对象):

class Drop_before:
  def __init__(self, val):
    self.val = val
    self.always_print = False
  def __call__(self, current_val):
    if current_val == self.val:
      self.always_print = True
    return self.always_print

drop_before_6 = Drop_before(6)
print [x for x in xrange(10) if drop_before_6(x)]
#using filter
print filter(Drop_before(4), xrange(10))

输出:

[6, 7, 8, 9]
[4, 5, 6, 7, 8, 9]