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

for*_*ran 149 python predicate

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

目前的代码非常难看:

[x for x in seq if predicate(x)][0]
Run Code Online (Sandbox Code Playgroud)

我想过把它改成:

from itertools import dropwhile
dropwhile(lambda x: not predicate(x), seq).next()
Run Code Online (Sandbox Code Playgroud)

但是必须有一些更优雅的东西...... None如果没有找到匹配,如果它返回一个值而不是引发异常会很好.

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

def get_first(predicate, seq):
    for i in seq:
        if predicate(i): return i
    return None
Run Code Online (Sandbox Code Playgroud)

但是开始使用像这样的实用程序函数来填充代码是非常无味的(并且人们可能不会注意到它们已经存在,所以如果存在已经提供相同的内置函数,它们往往会随着时间的推移而重复).

jfs*_*jfs 214

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

StopIteration如果没有,它会加注.

next(ifilter(predicate, seq), None)

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

  • 或者你可以为`next`提供第二个"默认"参数,而不是引发异常. (25认同)
  • `ifilter`在Python 3中被重命名为`filter`. (14认同)
  • @fortran:[`next()`](http://docs.python.org/library/functions.html#next)自Python 2.6起可用了你可以阅读[What's New page](http://docs.python) .org/whatsnew/2.7.html)快速熟悉新功能. (2认同)
  • @geekazoid:`seq.find(&method(:predicate))`或者甚至更简洁的实例方法,例如:`[1,1,4] .find(&:even?)` (2认同)

Che*_*wie 86

您可以使用具有默认值的生成器表达式,然后next:

next((x for x in seq if predicate(x)), None)
Run Code Online (Sandbox Code Playgroud)

虽然对于这种单行程,您需要使用Python> = 2.6.

这篇颇受欢迎的文章进一步讨论了这个问题:最干净的Python find-in-list函数?.


mac*_*mac 6

我认为您在问题中提出的两种解决方案都没有错。

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

(x for x in seq if predicate(x)).next()
Run Code Online (Sandbox Code Playgroud)

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

  • 在Python 3上,'generator'对象没有属性'next'`。 (5认同)

par*_*ty3 5

JF Sebastian 的答案是最优雅的,但正如 fortran 指出的那样需要 p​​ython 2.6。

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

from itertools import repeat,ifilter,chain
chain(ifilter(predicate,seq),repeat(None)).next()
Run Code Online (Sandbox Code Playgroud)

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

from itertools import islice,ifilter
list(islice(ifilter(predicate,seq),1))
Run Code Online (Sandbox Code Playgroud)

更新:虽然我个人使用一个名为first()的预定义函数来捕获StopIteration并返回None,但以下是对上面示例的可能改进:避免使用filter / ifilter:

from itertools import islice,chain
chain((x for x in seq if predicate(x)),repeat(None)).next()
Run Code Online (Sandbox Code Playgroud)

  • 哎呀!如果归根结底,我只会做一个简单的“for”循环,里面有一个“if”——更容易阅读 (20认同)