从符合条件的迭代中获取第一个项目

我想从匹配条件的列表中获得第一个项目。 所产生的方法不能处理整个列表是非常重要的,这个列表可能非常大。 例如,以下function就足够了:

def first(the_iterable, condition = lambda x: True): for i in the_iterable: if condition(i): return i 

这个函数可以这样使用:

 >>> first(range(10)) 0 >>> first(range(10), lambda i: i > 3) 4 

但是,我想不出一个好的内置/单线程来让我这样做。 我不特别想复制这个function,如果我不需要。 有没有一种内置的方式来获得匹配条件的第一个项目?

在Python 2.6或更高版本中:

如果您希望在找不到匹配元素的情况下引发StopIteration

next(x for x in the_iterable if x > 3)

如果你想要返回default_value (例如None ):

next( (x for x in the_iterable if x>3), default_value)

请注意,在这种情况下,在生成器expression式周围需要一对额外的括号 – 当生成器expression式不是唯一的参数时,总是需要它们。

我看到大多数的答案坚决地忽略了next内置的,所以我认为,由于某种神秘的原因,他们100%关注于版本2.5和更老版本 – 没有提到Python版本的问题(但是我没有看到提及在提到next内置的答案中,这就是为什么我认为有必要自己提供一个答案 – 至less“正确的版本”问题是这样logging的;-)。

在2.5中,迭代器的.next()方法立即引发StopIteration如果迭代器立即完成) – 即,对于您的用例,如果迭代器中没有项目满足条件。 如果你不在乎(即,你知道必须至less有一个令人满意的项目),那么只需使用.next() (最好在Genexp上,为Python 2.6中的next内置行更好)。

如果你真的关心,那么就像你最初在Q中指出的那样包装一个函数似乎是最好的,虽然你提出的函数实现很好,但你也可以使用itertoolsfor...: break循环或者genexp ,或try/except StopIteration作为函数的身体,作为各种答案build议。 在这些替代品中没有太多的附加价值,所以我会select你最初提出的非常简单的版本。

与使用ifilter类似,您可以使用生成器expression式:

 >>> (x for x in xrange(10) if x > 5).next() 6 

无论哪种情况,您可能都希望捕获StopIteration ,以防没有元素满足您的条件。

从技术上讲,我想你可以这样做:

 >>> foo = None >>> for foo in (x for x in xrange(10) if x > 5): break ... >>> foo 6 

这将避免必须try/except块。 但是,这似乎有点晦涩和辱骂的语法。

作为一个可重用,logging和testing的function

 def first(iterable, condition = lambda x: True): """ Returns the first item in the `iterable` that satisfies the `condition`. If the condition is not given, returns the first item of the iterable. Raises `StopIteration` if no item satysfing the condition is found. >>> first( (1,2,3), condition=lambda x: x % 2 == 0) 2 >>> first(range(3, 100)) 3 >>> first( () ) Traceback (most recent call last): ... StopIteration """ return next(x for x in iterable if condition(x)) 

对于下一个内build不存在的旧版本的Python:

 (x for x in range(10) if x > 3).next() 

itertools模块包含迭代器的过滤函数。 过滤迭代器的第一个元素可以通过调用next()获得:

 from itertools import ifilter print ifilter((lambda i: i > 3), range(10)).next() 

我会写这个

 next(x for x in xrange(10) if x > 3) 

通过使用

 (index for index, value in enumerate(the_iterable) if condition(value)) 

可以检查_iterable中第一个项的条件 ,并获取其索引,而不需要评估_iterable中的所有项。

完整的expression使用是

 first_index = next(index for index, value in enumerate(the_iterable) if condition(value)) 

这里first_index假设在上面讨论的expression式中识别的第一个值的值。

该死的例外!

我喜欢这个答案 。 但是,因为next()在没有项目时引发StopIterationexception,所以我会使用下面的代码片段来避免exception:

 a = [] item = next((x for x in a), None) 

例如,

 a = [] item = next(x for x in a) 

将引发StopIterationexception;

 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 

既然你已经请求了一个内置的单线程,这将避免StopIterationexception的问题,尽pipe它需要你的迭代很小,所以你可以把它投到列表中,因为这是我知道的唯一的构造将吞下StopIteration,让你偷看价值:

 (lambda x:x[0] if x else None)(list(y for y in ITERABLE if CONDITION)) 

(如果没有元素匹配,您将得到None而不是StopIterationexception。)

这个问题已经有了很好的答案。 我只是增加了我的两分钱,因为我登陆这里试图find解决我自己的问题,这是非常相似的OP。

如果您想使用生成器find符合条件的第一个项目的INDEX,可以简单地执行:

 next(index for index, value in enumerate(iterable) if condition) 

在Python 3中:

 a = (None, False, 0, 1) assert next(filter(None, a)) == 1 

在Python 2.6中:

 a = (None, False, 0, 1) assert next(iter(filter(None, a))) == 1 

Oneliner:

 thefirst = [i for i in range(10) if i > 3][0] 

如果您不确定任何元素将根据条件是否有效,则应该try/except因为[0]会引发IndexError