查找序列中与谓词匹配的第一个元素

我想要一种惯用的方法来查找匹配谓词的列表中的第一个元素。

当前的代码相当难看:

[x for x in seq if predicate(x)][0]

我想过把它改成:

from itertools import dropwhile
dropwhile(lambda x: not predicate(x), seq).next()

但肯定有更优雅的……如果它返回一个None值,而不是在没有找到匹配时引发异常,那就更好了。

我知道我可以这样定义一个函数:

def get_first(predicate, seq):
for i in seq:
if predicate(i): return i
return None

但是,如果已经内置了提供相同功能的内置功能,那么开始用这样的实用函数填充代码是相当乏味的(人们可能不会注意到它们已经存在,所以它们往往会随着时间的推移而重复出现)。

154755 次浏览

查找序列seq中与predicate匹配的第一个元素:

next(x for x in seq if predicate(x))

或者仅仅是:

Python 2:

next(itertools.ifilter(predicate, seq))

Python 3:

next(filter(predicate, seq))

如果谓词不匹配任何元素,将引发StopIteration异常。


如果没有这样的元素,则返回None:

next((x for x in seq if predicate(x)), None)

或者:

next(filter(predicate, seq), None)

你可以使用一个带有默认值的生成器表达式,然后next它:

next((x for x in seq if predicate(x)), None)

不过对于这一行代码,您需要使用Python >= 2.6。

这篇相当流行的文章进一步讨论了这个问题:最简洁的Python列表查找函数?

我不认为你提出的两个方案有什么问题。

在我自己的代码中,我会像这样实现它:

(x for x in seq if predicate(x)).next()

使用()的语法创建了一个生成器,这比使用[]一次性生成所有列表更有效。

J.F. Sebastian的回答是最优雅的,但正如fortran指出的那样,需要python 2.6。

对于Python版本<2.6,这是我能想到的最好的:

from itertools import repeat,ifilter,chain
chain(ifilter(predicate,seq),repeat(None)).next()

或者,如果你以后需要一个列表(list处理StopIteration),或者你需要的不仅仅是第一个,但仍然不是全部,你可以用islice来做:

from itertools import islice,ifilter
list(islice(ifilter(predicate,seq),1))
< p >更新: 虽然我个人使用一个被称为first()的预定义函数来捕获StopIteration并返回None,但这里有一个可能的改进:避免使用filter / filter:

from itertools import islice,chain
chain((x for x in seq if predicate(x)),repeat(None)).next()