我想从匹配条件的列表中获得第一项。产生的方法不能处理整个列表,这一点很重要,因为列表可能相当大。例如,以下函数就足够了:

def first(the_iterable, condition = lambda x: True):
    for i in the_iterable:
        if condition(i):
            return i

这个函数可以这样使用:

>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4

但是,我想不出一个好的内置/单行程序来让我这样做。如果没有必要,我不想复制这个函数。是否有一种内置的方法来获取匹配条件的第一项?


当前回答

我知道已经太迟了,但我的回答是:

def find_index(nums, fn):
    return next(i for i, x in enumerate(nums) if fn(x))
print(find_index([1, 2, 3, 4], lambda n: n % 2 == 1))

其他回答

itertools模块包含一个用于迭代器的过滤器函数。过滤迭代器的第一个元素可以通过调用next()来获得:

from itertools import ifilter

print ifilter((lambda i: i > 3), range(10)).next()

对于任何使用Python 3.8或更新版本的人,我建议使用PEP 572—赋值表达式中描述的“赋值表达式”。

if any((match := i) > 3 for i in range(10)):
    print(match)

下面是带有基准的3个备选方案。

使用next ()

一行程序:

values = list(range(1, 10000000))

value = next((x for x in values if x > 9999999), None)

使用函数

这是使用函数next()的替代方案,它大约快2%-5%:

values = list(range(1, 10000000))

def first(items):
    for item in items:
        if item > 9999999:  # Your condition
            return item
    return None  # Default value

value = first(values)

使用λ

这是一个在所有情况下都可用于替换next()的函数。性能大约降低300%:

values = list(range(1, 10000000))

def first(items, condition, default = None):
    for item in items:
        if condition(item):
            return item
    return default

value = first(values, lambda x: x > 9999999, None)

基准

功能:1 x 下:1.02 - 1.05 x Lambda: > 3x

内存消耗相同。

这就是基准。

该死的例外!

我喜欢Alex Martelli的回答。然而,由于next()在没有项目时抛出StopIteration异常, 我会使用下面的代码片段来避免异常:

a = []
item = next((x for x in a), None)

例如,

a = []
item = next(x for x in a)

将引发StopIteration异常;

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

对于不存在下一个内置的旧版本的Python:

(x for x in range(10) if x > 3).next()