find与谓词匹配的序列中的第一个元素
前面的愚蠢问题:我想要一个习惯的方式来find匹配谓词的列表中的第一个元素。
目前的代码是相当丑陋的:
[x for x in seq if predicate(x)][0]
我想过把它改成:
from itertools import dropwhile dropwhile(lambda x: not predicate(x), seq).next()
但是必须有更优雅的东西…如果它返回一个None
值,而不是在没有find匹配的情况下抛出exception,那将会很好。
我知道我可以定义一个函数,如:
def get_first(predicate, seq): for i in seq: if predicate(i): return i return None
但是用这样的效用函数来开始填充代码是相当无味的(如果已经提供了相同的内置函数,人们可能不会注意到它们已经在那里,所以它们往往会随着时间的推移而重复)。
next(x for x in seq if predicate(x))
如果没有,则会引发StopIteration
。
next(ifilter(predicate, seq), None)
如果没有这样的元素,则返回None
。
你可以使用一个默认值的生成器expression式,然后next
:
next((x for x in seq if predicate(x)), None)
虽然对于这一行,你需要使用Python> = 2.6。
这个颇受欢迎的文章进一步讨论了这个问题: 最干净的Python find-in-list函数? 。
我不认为你提出的解决scheme有什么问题。
在我自己的代码中,我会这样实现它:
(x for x in seq if predicate(x)).next()
用()
语法创build一个生成器,比用[]
一次生成所有列表更有效率。
JF塞巴斯蒂安的答案是最优雅的,但要求python 2.6 fortran指出。
对于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()