Python 从与条件匹配的iterable中获取第一项

Python 从与条件匹配的iterable中获取第一项,python,iterator,Python,Iterator,我想从匹配条件的列表中获取第一项。重要的是,生成的方法不能处理整个列表,因为整个列表可能非常大。例如,以下功能已足够: def first(the_iterable, condition = lambda x: True): for i in the_iterable: if condition(i): return i 此函数可用于以下情况: >>> first(range(10)) 0 >>> first(

我想从匹配条件的列表中获取第一项。重要的是,生成的方法不能处理整个列表,因为整个列表可能非常大。例如,以下功能已足够:

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
>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
... 
>>> foo
6
然而,我想不出一个好的内置/一个班轮让我这样做。如果不需要的话,我并不特别想复制这个函数。是否有一种内置方法来获取与条件匹配的第一项?

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

from itertools import ifilter

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

与使用ifilter类似,您可以使用生成器表达式:

>>> (x for x in xrange(10) if x > 5).next()
6
在这两种情况下,如果没有元素满足您的条件,您可能希望捕获
StopIteration

从技术上讲,我想你可以这样做:

>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4
>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
... 
>>> foo
6
它将避免生成
try/except
块。但这似乎有点晦涩难懂,对语法有点滥用。

我会写这篇文章

next(x for x in xrange(10) if x > 3)
Oneliner:

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

如果您不确定根据标准,任何元素都是有效的,那么应该将其包含在
try/except
中,因为
[0]
可以为不存在下一个内置元素的较旧版本的Python生成
索引器

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

在Python 2.6或更新版本中:

如果要在未找到匹配元素时引发
StopIteration

next(如果x>3,则x代表表中的x)
如果希望返回
默认值
(例如
):

next(如果x>3,则x代表参数中的x),默认值)
请注意,在本例中,需要在生成器表达式周围额外添加一对括号− 只要生成器表达式不是唯一的参数,就需要它们

我看到大多数答案都坚决忽略了内置的,因此我假设出于某种神秘的原因,他们100%地关注版本2.5和更高版本——而没有提到Python版本问题(但我没有看到答案中提到内置的
next
,这就是为什么我认为有必要自己提供答案——至少“正确版本”的问题是这样记录下来的;-)

在2.5中,如果迭代器立即完成,迭代器的方法会立即引发
StopIteration
,也就是说,对于您的用例,如果iterable中的任何项都不满足条件,那么就使用
。next()
(最适合genexp,用于Python 2.6及更高版本中内置的
next
行)

如果您真的在意,那么像您在Q中首先指出的那样将东西包装到函数中似乎是最好的,虽然您提出的函数实现很好,但是您也可以使用
itertools
、一个
for…:break
循环、一个genexp或一个
try/except StopIteration
作为函数的主体,作为各种答案建议。这些备选方案都没有太多附加值,所以我会选择你第一次提出的非常简单的版本。

该死的例外! 我喜欢。但是,由于
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
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
停止迭代
作为可重复使用、记录和测试的功能 具有默认参数的版本 @zorf建议使用此函数的一个版本,如果iterable为空或没有与条件匹配的项,则可以使用预定义的返回值:

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
a = [2,3,4,5,6,7,8]
assert next(filter(lambda x: x%2, a)) == 3
在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
编辑:我认为这很明显,但显然不是:您可以通过检查条件来传递函数(或
lambda
),而不是
None

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
a = [2,3,4,5,6,7,8]
assert next(filter(lambda x: x%2, a)) == 3
利用

(index for index, value in enumerate(the_iterable) if condition(value))
可以检查\u iterable中第一项的值的条件,并获得其索引,而无需评估\u iterable中的所有项

要使用的完整表达式是

first_index = next(index for index, value in enumerate(the_iterable) if condition(value))

这里first_index假设上面讨论的表达式中确定的第一个值的值。

这个问题已经有了很好的答案。我只添加了我的两分钱,因为我在这里试图找到我自己问题的解决方案,这与OP非常相似

如果要使用生成器查找与条件匹配的第一项的索引,只需执行以下操作:

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

Python 3中最有效的方法是以下方法之一(使用类似的示例):

“理解”风格: 警告:该表达式也适用于Python2,但在示例中使用了
range
,它在Python3中返回一个iterable对象,而不是像Python2那样的列表(如果要在Python2中构造iterable,请使用
xrange

请注意,表达式避免在理解表达式
next([i for…])
中构造列表,这将导致在筛选元素之前创建包含所有元素的列表,并导致处理整个选项,而不是停止迭代一次
i==1000

“功能性”风格: 警告:这在Python2中不起作用,甚至将
range
替换为
xrange
,因为
filter
创建了一个列表而不是迭代器(效率低下),而
next
函数只适用于迭代器

默认值 如其他响应中所述,如果要避免在条件未满足时引发异常,则必须向函数
next
添加额外参数

“功能性”风格: “理解”风格
next((i for i in range(100000000) if i == 1000), False)
import numpy as np
l = list("helloworld") # Create list
i = np.argwhere(np.array(l)=="l") # i = array([[2],[3],[8]])
index_of_first = i.min()
import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_first = i.min()
import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_last = i.max()
if any((match := i) > 3 for i in range(10)):
    print(match)