我想要一种惯用的方法来查找列表中与谓词匹配的第一个元素。
目前的代码非常难看:
[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
但是开始使用这样的实用程序函数填充代码是非常无味的(并且人们可能不会注意到它们已经存在,所以如果有已经提供相同的内置函数,它们会随着时间的推移而重复)
答案 0 :(得分:197)
next(x for x in seq if predicate(x))
如果没有,则会引发StopIteration
。
next(ifilter(predicate, seq), None)
返回None
。
答案 1 :(得分:83)
您可以使用具有默认值的生成器表达式,然后使用next
它:
next((x for x in seq if predicate(x)), None)
虽然对于这个单行,你需要使用Python> = 2.6。
这篇颇受欢迎的文章进一步讨论了这个问题:Cleanest Python find-in-list function?。
答案 2 :(得分:5)
我认为您在问题中提出的任何解决方案都没有任何问题。
在我自己的代码中,我会像这样实现它:
(x for x in seq if predicate(x)).next()
使用()
的语法会创建一个生成器,这比使用[]
一次生成所有列表更有效。
答案 3 :(得分:1)
J.F。塞巴斯蒂安的答案是最优雅的,但需要python 2.6,就像他指出的那样。
对于Python版本< 2.6,这是我能想到的最好的:
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()