我有一个list
,想要构建(通过理解)另一个列表。我希望这个新列表的大小有限,通过条件
以下代码将失败:
a = [1, 2, 1, 2, 1, 2]
b = [i for i in a if i == 1 and len(b) < 3]
与
Traceback (most recent call last):
File "compr.py", line 2, in <module>
b = [i for i in a if i == 1 and len(b) < 3]
File "compr.py", line 2, in <listcomp>
b = [i for i in a if i == 1 and len(b) < 3]
NameError: name 'b' is not defined
因为b
在构建理解时未定义尚未。
有没有办法在构建时限制新列表的大小?
注意:当达到计数器时,我可以使用正确的for
将理解分解为break
循环,但我想知道是否存在使用理解的机制。
答案 0 :(得分:6)
@Martijn Pieters完全正确,itertools.islice
是解决此问题的最佳方法。但是,如果您不介意其他(外部)库,则可以使用iteration_utilities
包含大量这些itertools
及其应用程序(以及其他一些应用程序)。它可以使这更容易,至少如果你喜欢函数式编程:
>>> from iteration_utilities import Iterable
>>> Iterable([1, 2, 1, 2, 1, 2]).filter((1).__eq__)[:2].as_list()
[1, 1]
>>> (Iterable([1, 2, 1, 2, 1, 2])
... .filter((1).__eq__) # like "if item == 1"
... [:2] # like "islice(iterable, 2)"
... .as_list()) # like "list(iterable)"
[1, 1]
iteration_utilities.Iterable
类在内部使用生成器,因此在您调用任何as_*
(或get_*
)方法之前,它只会处理所需数量的项目。
免责声明:我是the iteration_utilities
library的作者。
答案 1 :(得分:3)
当计数器达到所需的整数(在这种情况下为itertools.count
)时,可以使用itertools.takewhile
生成计数器和3
来停止迭代生成器:
from itertools import count, takewhile
c = count()
b = list(takewhile(lambda x: next(c) < 3, (i for i in a if i == 1)))
或者类似的想法构建一个构造来引发StopIteration
以终止生成器。这是您最接近breaking the list comprehension的原始想法,但我不建议将其作为最佳做法:
c = count()
b = list(i if next(c) < 3 else next(iter([])) for i in a if i == 1)
示例:
>>> a = [1,2,1,4,1,1,1,1]
>>> c = count()
>>> list(takewhile(lambda x: next(c) < 3, (i for i in a if i == 1)))
[1, 1, 1]
>>> c = count()
>>> list(i if next(c) < 3 else next(iter([])) for i in a if i == 1)
[1, 1, 1]
答案 2 :(得分:0)
itertools.slice
是从生成器中提取 n 项的自然方法。
但您也可以使用辅助函数自行实现。就像StopIteration
pseudo-code一样,我们会抓住def take_n(gen, n):
for _ in range(n):
try:
yield next(gen)
except StopIteration:
break
g = (i**2 for i in range(5))
res = list(take_n(g, 20))
print(res)
[0, 1, 4, 9, 16]
来限制产生的商品数量。
这更具适应性,因为如果 n 大于生成器中的项目数,它允许您指定逻辑。
{{1}}
答案 3 :(得分:0)
没有islice
的相同解决方案:
filtered = (i for i in a if i == 1)
b = [filtered.next() for j in range(3)]
顺便说一句,如果生成器为空或小于3,请注意-您将收到 StopIteration异常。
为防止这种情况,您可能需要使用具有默认值的next()。例如:
b = [next(filtered, None) for j in range(3)]
如果您不想在列表中“无”:
b = [i for i in b if i is not None]
答案 4 :(得分:-3)
使用枚举:
b = [n for i,n in enumerate(a) if n==1 and i<3]