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


170

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

当前代码非常丑陋:

[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

但是,如果已经有内置的插件开始用这样的实用函数填充代码,这是很鸡肋的(人们可能不会注意到它们已经在那里,因此随着时间的推移它们会不断重复出现)。


2
除了比“ python sequence find function ” 晚问,这个问题的标题要好得多

Answers:


249

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

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

itertools.ifilter在Python 2上)

next(filter(predicate, seq))

StopIteration如果没有,它将引发。


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

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

要么:

next(filter(predicate, seq), None)

27
或者,您可以为其提供第二个“默认”参数next,而不是引发异常。
Karl Knechtel

2
@fortran:next()自Python 2.6起可用。您可以阅读新增功能”页面以快速熟悉新功能。
jfs

1
我是python新手,请阅读文档,而ifilter使用“ yield”方法。我认为这意味着谓词在我们进行时会被延迟评估。即,我们不会在整个列表中运行谓词,因为我有一个谓词函数,该函数有点贵,我只想迭代直到找到一个项目为止
Kannan Ekanath 2012年

2
@geekazoid:seq.find(&method(:predicate))或更简洁的实例方法,例如:[1,1,4].find(&:even?)
jfs

16
ifilter更名为filter在Python 3
tsauerwein

92

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

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

尽管对于这种单行代码,您需要使用Python> = 2.6。

这篇颇受欢迎的文章进一步讨论了这个问题:最干净的Python在列表中查找功能?


8

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

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

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

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


不仅如此- []如果迭代器
永无休止

6
'generator' object has no attribute 'next'在Python 3上
。– jfs

@glglgl-关于第一个要点(永无止境),我对此表示怀疑,因为该参数是一个有限序列[更确切地说是根据OP问题的列表]。至于第二个:同样,由于提供的参数是一个序列,因此在调用此函数时应该已经创建并存储了对象……还是我遗漏了什么?
mac

@JFSebastian-谢谢,我没有意识到!:)出于好奇,此选择背后的设计原理是什么?
mac

@mac-与其他特殊方法的双下划线保持一致。参见python.org/dev/peps/pep-3114
Chewie

1

JF Sebastian的答案是最优雅的,但需要fortran指出的python 2.6。

对于2.6以下的Python版本,这是我能提供的最好的:

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

另外,如果您以后需要一个列表(列表处理StopIteration),或者您需要的不仅仅是第一个列表,但还不是全部,您可以使用islice来实现:

from itertools import islice,ifilter
list(islice(ifilter(predicate,seq),1))

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

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

11
kes!如果它归结到这一点,我只想做简单的“for”循环使用“如果”里面-更容易阅读
尼克·帕金斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.