在列表内涵里使用 break

我如何根据一个条件(例如当找到数字 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]

I 真的喜欢:

  • 一句俏皮话
  • 没有其他花哨的库,比如 itertools,“纯 Python”(如果可能的话,请阅读: 解决方案不应该使用任何 import语句或类似的语句)
63910 次浏览

如果412肯定在这个列表中,你可以使用以下内容:

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

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

注意,由于切片的原因,这将比迭代工具或 for 循环解决方案更低效(至少在内存方面)。

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

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

编辑 : 我刚注意到不要使用任何 imports 的要求。

使用一个函数来提高 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)

对于那些抱怨它是骇客和不应该在产品代码中使用: 嗯,你是对的。 当然。

列表显示(包括列表理解)的语法如下: 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))

(代码取自斯文 · 马尔纳的回答,在我打字的时候贴出来的)。

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

只是把 F.J. 的代码放在上面,然后添加一个三进制来检查412是否在列表中。仍然是一个“一行程序”,即使412不在列表中也会工作。

我知道这是一个非常古老的职位,但因为 OP 问使用 break内的 list-comprehension和我也在寻找类似的东西,我想我会在这里发表我的发现,以供将来参考。

在研究 break的过程中,我偶然发现了 iter的一个鲜为人知的特性—— iter(callable, sentinel),它返回一个迭代器,即 “休息”迭代一次可调用的 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.

棘手的部分是定义一个适合给定问题的函数。在这种情况下,首先我们需要将给定的 numberslist转换为使用 x = iter(numbers)iterator,它作为外部变量输入到 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]

另一个鬼鬼祟祟的一行解决方案来解决 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 所说:

对于那些抱怨它是骇客和不应该在产品代码中使用: 嗯,你是对的。 当然。

考虑到生成器解决方案已经过时,我想出了以下方法:

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]

有一次我遇到了类似的问题,答案是:

next((x for x in [1, 2, 3, 4] if x % 2 == 0), [])

最后一个 []需要作为默认值,以防止找不到 StopIteration错误

或者

any(print(x) if x < 2 else True for x in range(5))

为了证明,他返回 None(逻辑上是 False)。