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

时间:2015-01-08 13:52:23

标签: 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

虽然这很好用,但我尝试使用列表推导做同样的事情。这就是我得到的:

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]

这显然不起作用,因为当找到所需的字符串时我没有设置always_print = True。我如何在列表理解中做到这一点?

3 个答案:

答案 0 :(得分:8)

使用itertools.dropwhile()查找包含def set的第一行:

from itertools import dropwhile

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

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

dropwhile()返回一个迭代器;我在这里使用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']

答案 1 :(得分:2)

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

不是说我会在生产代码中使用它,而是在黑客攻击它,你可以在列表理解中做到这一点......

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]

只是为了解释,三个条件中的第二个总是返回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
             )]

<强> EDIT2

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

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

  1. 在Python中,大多数对象和值都有一个关联的布尔值。例如0为False,任何其他数字为True。同样,空列表为False,非空列表为True
  2. 虽然a = 1之类的变量赋值不会返回任何值,也不能作为列表推导的一部分包含在内,a_list.append(x)是一个函数调用并返回None,其被评估为False }。它还有将新元素x添加到a_list末尾的副作用。
  3. andor等逻辑运算符从左到右都有评估顺序。 and在第一个False值处停止执行,or在第一个True值处停止执行,该值可用于控制是否根据某些值执行列表追加条件。三元运算符&#39; x if y else z&#39;也有评估顺序,但评估&#39; y&#39;首先是&#39; x&#39;或者&#39; z&#39;但从来没有。
  4. 正如您所看到的,一组非常迂回的逻辑技巧可能在超优化的C(或IOCCC)中占有一席之地,但不是Python。 可能可以在列表理解中复制您的控制流,但实际上,每次都使用dropwhile

答案 2 :(得分:0)

掉线可能是最好的解决方案。但如果你想要一些非常有趣的东西,请看看以下内容:

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

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]