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如果没有这样的元素则返回.
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函数?.
我认为您在问题中提出的两种解决方案都没有错。
在我自己的代码中,我会这样实现:
(x for x in seq if predicate(x)).next()
Run Code Online (Sandbox Code Playgroud)
使用的语法()创建了一个生成器,它比使用一次生成所有列表的效率更高[]。
JF Sebastian 的答案是最优雅的,但正如 fortran 指出的那样需要 python 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)