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

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

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


当前回答

在Python 3中:

a = (None, False, 0, 1)
assert next(filter(None, a)) == 1

在Python 2.6中:

a = (None, False, 0, 1)
assert next(iter(filter(None, a))) == 1

编辑:我认为这是显而易见的,但显然不是:而不是None,你可以传递一个函数(或一个lambda),检查条件:

a = [2,3,4,5,6,7,8]
assert next(filter(lambda x: x%2, a)) == 3

其他回答

Oneliner:

thefirst = [i for i in range(10) if i > 3][0]

如果你不确定根据条件,任何元素都是有效的,你应该用try/except将其括起来,因为[0]可能引发IndexError。

这个问题已经有了很好的答案。我只是说说我的意见,因为我来这里是想为我自己的问题找到一个解决方案,这和OP非常相似。

如果你想使用生成器找到匹配条件的第一项的INDEX,你可以简单地这样做:

next(index for index, value in enumerate(iterable) if condition)

作为一个可重用、文档化和测试的函数

def first(iterable, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    Raises `StopIteration` if no item satysfing the condition is found.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    """

    return next(x for x in iterable if condition(x))

带有默认参数的版本

@zorf建议这个函数的一个版本,如果可迭代对象为空或没有匹配条件的项,你可以有一个预定义的返回值:

def first(iterable, default = None, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    If the `default` argument is given and the iterable is empty,
    or if it has no items matching the condition, the `default` argument
    is returned if it matches the condition.

    The `default` argument being None is the same as it not being given.

    Raises `StopIteration` if no item satisfying the condition is found
    and default is not given or doesn't satisfy the condition.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    >>> first([], default=1)
    1
    >>> first([], default=1, condition=lambda x: x % 2 == 0)
    Traceback (most recent call last):
    ...
    StopIteration
    >>> first([1,3,5], default=1, condition=lambda x: x % 2 == 0)
    Traceback (most recent call last):
    ...
    StopIteration
    """

    try:
        return next(x for x in iterable if condition(x))
    except StopIteration:
        if default is not None and condition(default):
            return default
        else:
            raise

下面是带有基准的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

内存消耗相同。

这就是基准。

在Python 3中:

a = (None, False, 0, 1)
assert next(filter(None, a)) == 1

在Python 2.6中:

a = (None, False, 0, 1)
assert next(iter(filter(None, a))) == 1

编辑:我认为这是显而易见的,但显然不是:而不是None,你可以传递一个函数(或一个lambda),检查条件:

a = [2,3,4,5,6,7,8]
assert next(filter(lambda x: x%2, a)) == 3