如何返回传递测试的迭代器的前n项,而不对剩余部分运行测试

时间:2016-08-10 21:46:58

标签: python listiterator

我正在寻找类似的东西:

foo = [ p for p in somelist if xxx(p)][0:10]

这可行,但在所有列表上执行xxx,xxx很昂贵。

我可以编写一个子函数,例如

def blah (list, len): 
    res=[]; i=0; 
    while i<len: 
        if xxx(list[i]): 
            res.append(i) 
   return res

但这似乎非常不合情理。

证明第一个结果的例子是错误的:

foo = [ p for p in (1,2,3,4,5,0,6) if 100/p ][0:3]

哪个“应该”返回[1,2,3]但实际上在除以零时失败。

我已经尝试过itertools的各种工具但找不到在达到大小后停止执行迭代器的组合。

3 个答案:

答案 0 :(得分:2)

尝试itertools.islice

from itertools import islice
foo = list(islice((p for p in (1,2,3,4,5,0,6) if 100/p), 4))

请注意缺少括号:这是generator comprehension

答案 1 :(得分:1)

这与列表推导无关,但可以通过生成器理解来实现。

两者有什么区别?列表推导将一次迭代迭代,并根据您对iterable上项目的操作将列表返回给您。这里的关键是 all all

您的示例使用列表推导,然后会发生什么:首先评估列表推导。在你的情况下,它会失败,但即使它没有失败,它也会迭代迭代somelist中的所有内容,并且会返回结果列表。然后将此列表切片并返回新的结果列表。

发电机理解和发电机一般有不同的工作方式。它们基本上是暂停的代码块,直到您从它们请求更多数据,这是您真正想要做的。

实际上,您可以按如下方式创建生成器:

g = (p for p in (1,2,3,4,5,0,6) if 100/p )

根据&#34;规则&#34>您现在有一个生成器,当您请求时,它将为您生成值。你给了它。

只要您手头有一台发电机,就可以通过多种方式从中获取n项目。

您可以按如下方式编写一个简单的for循环:

results = []
for x in range(3): # n = 3 in your case
    results.append(next(g))

当然,这不是Pythonic。假设您想要一个列表,您现在可以使用列表解析:

results = [next(g) for x in range(3)]

这是&#34;手册&#34;这样做的方式。您还可以使用islice模块(Documentation here)中的itertools函数:

import itertools
results = list(itertools.islice(g, 4))

就是这样。生成器非常有用。在请求并记住其状态时执行的代码块是非常宝贵的。

答案 2 :(得分:0)

通过使用内置过滤器和islice的组合,您可以实现您想要的效果。 例如

length = 3
items_iter = islice(filter(None, [1, 2, 0, 4, 5]), length) # returns a lazy iterator
items = list(items_iter)
assert items == [1, 2, 5]

但是,您可能希望编写自己的生成器。生成器函数将生成连续项,而不是返回单个结果。有时它可能不会产生任何物品,有时它们会产生无限数量的物品。以下是您的案例示例:

def take_first_n_that_pass(test, length, iterator):
    i = 0
    for item in iterator:
        if i >= length:
            return # stops generator
        if test(item):
            yield item
            i += 1
    # end of iterator -- generator stops here as well

items_iter = take_first_n_that_pass(bool, 3, [0, 1, 2, 0, 4, 5])
items = list(items_iter)
assert items == [1, 2, 4]