在列表推导中使用 break
- 2025-02-21 08:48:00
- admin 原创
- 26
问题描述:
如何根据某个条件(例如,当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
语句或类似的东西)
解决方案 1:
使用函数来引发StopIteration
并list
捕获它:
>>> 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)
对于那些抱怨它是黑客行为并且不应该在生产代码中使用的人来说:嗯,你是对的。绝对如此。
解决方案 2:
您可以将生成器表达式与以下函数一起使用itertools.takewhile()
:
even_numbers = (n for n in numbers if not n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))
编辑:我刚刚注意到不要使用任何import
s。好吧,无论如何我都会把这个答案留在这里。
解决方案 3:
我知道这是一个非常古老的帖子,但是因为 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.
这里比较棘手的部分是定义一个适合给定问题的函数。在这种情况下,首先我们需要将给定的转换list
为numbers
使用iterator
,x = 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]
解决方案 4:
even = [n for n in numbers[:None if 412 not in numbers else numbers.index(412)] if not n % 2]
只需采用上述 FJ 的代码并添加三元组来检查 412 是否在列表中。仍然是“一行代码”,即使 412 不在列表中也能正常工作。
解决方案 5:
如果 412 肯定会在列表中,您可以使用以下命令:
even = [n for n in numbers[:numbers.index(412)] if not n % 2]
如果您想在结果中包含 412,只需使用numbers[:numbers.index(412)+1]
切片即可。
请注意,由于切片,这将比 itertools 或 for 循环解决方案效率更低(至少在内存方面)。
解决方案 6:
列表显示(包括列表推导)的语法在这里:http://docs.python.org/reference/expressions.html#list-displays
如您所见,没有特殊的while
或until
语法。最接近的语法是:
even_numbers = (n for n in numbers if 0 == n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))
(代码取自 Sven Marnach 的回答,是我在输入这段内容时发布的)。
解决方案 7:
有一次我在网上遇到一个类似的问题,答案是:
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))
print
为了证明这一点,他又回来了None
(合乎逻辑False
)。
解决方案 8:
另一个巧妙的单行解决方案是在条件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 所说:
对于那些抱怨它是黑客行为并且不应该在生产代码中使用的人来说:嗯,你是对的。绝对如此。
解决方案 9:
考虑到生成器解决方案已经过时,我想出了以下解决方案:
even = [n for n in next((numbers[:i] for i, n in enumerate(numbers) if n == 412)) if not n % 2]
然后我回去看到了 Andrew Clark 的答案,虽然一样,但好多了
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]
解决方案 10:
我遇到了与破坏列表理解相同的问题。正如许多人所说,自 Python 3.5 以来,PEP 479的最高投票解决方案已经过时:StopIteration
从生成器内部抛出的 将被转换为RuntimeError
,而不是结束生成器本身。
我目前使用的是Python 3.11.4,其他所有解决方案(列表切片itertools.takewhile
、短路any
、断路器iter
和end
@recnac 的东西)仍然有效。
我发现takewhile
最优雅和最 Pythonic 的方法,但不知怎的,我想写一些该死的单行代码,实际上跳出列表而不是跳过列表的其余部分,但这似乎是无法实现的。