打破清单理解

时间:2012-03-05 19:40:16

标签: python list-comprehension

如何根据条件中断列表理解,例如找到数字412时?

代码:

numbers = [951, 402, 984, 651, 360, 69, 408, 319, 601, 485, 980, 507, 725, 547, 544,
           615, 83, 165, 141, 501, 263, 617, 865, 575, 219, 390, 984, 592, 236, 105, 942, 941,
           386, 462, 47, 418, 907, 344, 236, 375, 823, 566, 597, 978, 328, 615, 953, 345, 399,
           162, 758, 219, 918, 237, 412, 566, 826, 248, 866, 950, 626, 949, 687, 217, 815, 67,
           104, 58, 512, 24, 892, 894, 767, 553, 81, 379, 843, 831, 445, 742, 717, 958, 609, 842,
           451, 688, 753, 854, 685, 93, 857, 440, 380, 126, 721, 328, 753, 470, 743, 527]

even = [n for n in numbers if 0 == n % 2]

从功能上讲,这可能是你可以推断的:

even = [n for n in numbers if 0 == n % 2 and break if n == 412]

真的更喜欢:

  • 单行
  • 没有其他花哨的库,如itertools,“纯python”,如果可能的话(阅读:解决方案不应该使用任何import语句或类似)

8 个答案:

答案 0 :(得分:49)

使用函数引发StopIterationlist以捕获它:

>>> def end_of_loop():
...     raise StopIteration
... 
>>> even = list(end_of_loop() if n == 412 else n for n in numbers if 0 == n % 2)
>>> print(even)
[402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418, 344, 236, 566, 978, 328, 162, 758, 918]

对于抱怨的人来说,这不是一个单行:

even = list(next(iter(())) if n == 412 else n for n in numbers if 0 == n % 2)

对于那些抱怨的人来说,它是hackish,不应该用在生产代码中:嗯,你是对的。的绝对

答案 1 :(得分:42)

您可以将生成器表达式与itertools.takewhile()一起使用:

even_numbers = (n for n in numbers if not n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))

修改:我刚刚注意到不要使用任何import的要求。好吧,无论如何我都把这个答案留在这里。

答案 2 :(得分:13)

如果412肯定会在列表中,您可以使用:

even = [n for n in numbers[:numbers.index(412)] if not n % 2]

如果您想在结果中包含412,只需使用numbers[:numbers.index(412)+1]作为切片。

请注意,由于切片,这将比itertools或for循环解决方案效率低(至少在内存方面)。

答案 3 :(得分:12)

even = [n for n in numbers[:None if 412 not in numbers else numbers.index(412)] if not n % 2] 

刚刚接受上面的F.J.代码,并添加了一个三元组来检查412是否在列表中。即使412不在列表中,仍然是“一个班轮”并且仍然有效。

答案 4 :(得分:12)

我知道这是一篇非常古老的帖子,但是由于OP询问在break中使用list-comprehension并且我也在寻找类似的东西,我想我会在此发布我的发现以供将来参考

在调查break时,我发现了iter作为iter(callable, sentinel)的鲜为人知的特征,它返回一个迭代器,一旦可调用{{1}就会“中断”迭代} value等于function值。

sentinel

这里的棘手部分是定义一个适合给定问题的函数。在这种情况下,首先我们需要使用>>> help(iter) Help on built-in function iter in module __builtin__: iter(...) iter(collection) -> iterator iter(callable, sentinel) -> iterator Get an iterator from an object. In the first form, the argument must supply its own iterator, or be a sequence. In the second form, the callable is called until it returns the sentinel. 将给定的list numbers转换为iteratorx = iter(numbers)将外部变量作为lambda函数提供。

接下来,我们的可调用函数只是调用迭代器来吐出下一个值。然后迭代器与我们的标记值(在这种情况下为412)进行比较,并在达到该值时“中断”。

print [i for i in iter(lambda x=iter(numbers): next(x),412) if i %2 == 0]

>>> 
[402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418,  
 344, 236, 566, 978, 328, 162, 758, 918]

答案 5 :(得分:4)

列表显示的语法(包括列表推导)在这里:http://docs.python.org/reference/expressions.html#list-displays

如您所见,没有特殊的whileuntil语法。你能得到的最接近的是:

even_numbers = (n for n in numbers if 0 == n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))

(代码取自Sven Marnach的回答,在我输入时发布)。

答案 6 :(得分:0)

breaking in list comprehension条件的帮助下,另一种偷偷摸摸的单行解决方案来解决end

不使用numbers.index(412),也许快一点吗?

even = [n for end in [[]] for n in numbers
        if (False if end or n != 412 else end.append(42))
        or not end and not n % 2]

  

注意:这是一个坏主意。 只是为了好玩:)

如@WolframH所说:

  

对于那些抱怨它是hackish的人,不应该在生产代码中使用:嗯,您是对的。 绝对。

答案 7 :(得分:0)

考虑到发电机解决方案已经过时,我想出了以下几点:

even = [n for n in next((numbers[:i] for i, n in enumerate(numbers) if n == 412)) if not n % 2]

然后我回去看到安德鲁克拉克的答案,这是相同的但更好

even = [n for n in numbers[:numbers.index(412)] if not n % 2]

尽管切片解决方案的最佳部分是您可以选择在结束元素的任一侧包含或排除多个元素,例如获得 412 和后面的数字:

even = [n for n in numbers[:numbers.index(412)+2] if not n % 2]