如何限制理解的大小?

时间:2017-02-22 13:57:43

标签: python list-comprehension

我有一个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循环,但我想知道是否存在使用理解的机制。

5 个答案:

答案 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]