标签归档:predicate

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

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

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

当前代码非常丑陋:

[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

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

I want an idiomatic way to find the first element in a list that matches a predicate.

The current code is quite ugly:

[x for x in seq if predicate(x)][0]

I’ve thought about changing it to:

from itertools import dropwhile
dropwhile(lambda x: not predicate(x), seq).next()

But there must be something more elegant… And it would be nice if it returns a None value rather than raise an exception if no match is found.

I know I could just define a function like:

def get_first(predicate, seq):
    for i in seq:
        if predicate(i): return i
    return None

But it is quite tasteless to start filling the code with utility functions like this (and people will probably not notice that they are already there, so they tend to be repeated over time) if there are built ins that already provide the same.


回答 0

要查找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)

To find first element in a sequence seq that matches a predicate:

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

Or (itertools.ifilter on Python 2):

next(filter(predicate, seq))

It raises StopIteration if there is none.


To return None if there is no such element:

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

Or:

next(filter(predicate, seq), None)

回答 1

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

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

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

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

You could use a generator expression with a default value and then next it:

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

Although for this one-liner you need to be using Python >= 2.6.

This rather popular article further discusses this issue: Cleanest Python find-in-list function?.


回答 2

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

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

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

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

I don’t think there’s anything wrong with either solutions you proposed in your question.

In my own code, I would implement it like this though:

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

The syntax with () creates a generator, which is more efficient than generating all the list at once with [].


回答 3

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()

J.F. Sebastian’s answer is most elegant but requires python 2.6 as fortran pointed out.

For Python version < 2.6, here’s the best I can come up with:

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

Alternatively if you needed a list later (list handles the StopIteration), or you needed more than just the first but still not all, you can do it with islice:

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

UPDATE: Although I am personally using a predefined function called first() that catches a StopIteration and returns None, Here’s a possible improvement over the above example: avoid using filter / ifilter:

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