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

Chr*_*ips 264 python iterator

我想从符合条件的列表中获取第一项.重要的是,生成的方法不会处理整个列表,这可能非常大.例如,以下功能就足够了:

def first(the_iterable, condition = lambda x: True):
    for i in the_iterable:
        if condition(i):
            return i
Run Code Online (Sandbox Code Playgroud)

这个函数可以用这样的东西:

>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4
Run Code Online (Sandbox Code Playgroud)

但是,我想不出一个好的内置/单线来让我这样做.如果我不需要,我不特别想复制这个功能.是否有内置方法可以使第一个项目符合条件?

Ale*_*lli 415

在Python 2.6或更高版本中:

如果您想要StopIteration在没有找到匹配元素的情况下被引发:

next(x for x in the_iterable if x > 3)

如果你想要default_value(例如None)返回:

next( (x for x in the_iterable if x>3), default_value)

请注意,在这种情况下,您需要在生成器表达式周围添加一对括号 - 当生成器表达式不是唯一参数时,它们总是需要它们.

我看到大多数答案都坚决地忽略了next内置内容,所以我认为出于一些神秘的原因,他们100%专注于2.5及更早的版本 - 没有提到Python版本的问题(但后来我没有提到确实提到next内置的答案,这就是我认为有必要自己提供答案的原因 - 至少"正确的版本"问题以这种方式记录在案;-).

在2.5中,如果迭代器立即完成.next(),迭代器的方法会立即引发StopIteration- 例如,对于您的用例,如果迭代中没有项满足条件.如果你不关心(也就是说,你知道必须至少有一个令人满意的项目)那么只需使用.next()(最好使用genexp,next在Python 2.6中更好地使用内置行).

如果你护理,包裹的东西在一个功能,你就先在自己的Q指示,似乎最好的,当你提出的功能实现就好了,你可以选择使用itertools,一个for...: break循环或genexp,或try/except StopIteration作为函数体,如各种答案所示.这些替代方案中没有太多附加值,所以我会选择你最初提出的简单版本.

  • 不像你描述的那样工作.当没有找到元素时,它会引发"StopIteration" (5认同)
  • 由于这是选定的答案,我觉得有必要分享正确选择第一个元素的答案[这里](http://stackoverflow.com/a/35370041/916568).简而言之:不应鼓励使用next. (4认同)
  • @guyarad 在该答案中提出的解决方案如何比仅使用 next 更不“神秘”?反对 next (在那个答案中)的唯一论点是您必须处理异常;真的吗 ? (3认同)
  • 它是解析整个数组,还是在第一个匹配元素处停止(对于效率非常重要)? (2认同)

Car*_*orc 22

作为可重复使用,记录和测试的功能

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))
Run Code Online (Sandbox Code Playgroud)

  • 如果用方法包装它,至少捕获StopIteration并引发EmptySequence错误.没有元素时会更漂亮. (6认同)
  • @guyarad`StopIteration`是python中规范的"out of elements"异常.我没有看到被抛出的问题.我可能会使用默认值"None",它可以作为默认参数传递给函数. (2认同)
  • Baldrickk 我觉得这不是一种迭代方法。你不会在迭代器竞赛中调用这个。但我对此并没有太强烈的感觉:) (2认同)

Jos*_*ush 19

该死的例外!

我喜欢这个答案.但是,由于在没有项目时next()引发StopIteration异常,我将使用以下代码段来避免异常:

a = []
item = next((x for x in a), None)
Run Code Online (Sandbox Code Playgroud)

例如,

a = []
item = next(x for x in a)
Run Code Online (Sandbox Code Playgroud)

会引发StopIteration例外;

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
Run Code Online (Sandbox Code Playgroud)


Mat*_*son 13

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

>>> (x for x in xrange(10) if x > 5).next()
6
Run Code Online (Sandbox Code Playgroud)

在任何一种情况下,你可能想要捕捉StopIteration,以防没有元素满足你的条件.

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

>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
... 
>>> foo
6
Run Code Online (Sandbox Code Playgroud)

它可以避免不得不try/except阻止.但这似乎有点模糊和滥用语法.

  • 最后一个根本不干净 - 对于genex中的foo:break`只是一种做foo = next(genex)`的方法,而不会明确赋值,如果操作没有,则会引发异常.被压扁是有道理的.结束使用失败代码而不是捕获异常通常是Python中的*坏事. (5认同)

Mar*_*uiz 9

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

具有“领悟”风格:

next(i for i in range(100000000) if i == 1000)
Run Code Online (Sandbox Code Playgroud)

警告:该表达式也适用于Python 2,但在该示例中使用的range是在Python 3中返回一个可迭代对象,而不是像Python 2这样的列表(如果要在Python 2中构造一个可迭代对象,请使用xrange)。

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

具有“实用”风格:

next(filter(lambda i: i == 1000, range(100000000)))
Run Code Online (Sandbox Code Playgroud)

警告:这在Python 2中不起作用rangexrange由于filter创建列表而不是迭代器(效率低下)而无法替换,并且该next函数仅适用于迭代器。

默认值

如其他响应中所述,next如果要避免在不满足条件时引发异常,则必须在函数中添加一个额外参数。

“实用”风格:

next(filter(lambda i: i == 1000, range(100000000)), False)
Run Code Online (Sandbox Code Playgroud)

“领悟”风格:

使用这种样式时,您需要将comprehension表达式包含()在其中,以避免出现SyntaxError: Generator expression must be parenthesized if not sole argument

next((i for i in range(100000000) if i == 1000), False)
Run Code Online (Sandbox Code Playgroud)


sth*_*sth 6

itertools模块包含迭代器的过滤器函数.可以通过调用next()它来获取筛选迭代器的第一个元素:

from itertools import ifilter

print ifilter((lambda i: i > 3), range(10)).next()
Run Code Online (Sandbox Code Playgroud)

  • 生成器表达式更简单. (2认同)
  • (`i`)`filter` 和 (`i`)`map` 在应用的函数已经存在的情况下可能有意义,但在这种情况下,仅使用生成器表达式更有意义。 (2认同)

Mik*_*ham 6

我会写这个

next(x for x in xrange(10) if x > 3)
Run Code Online (Sandbox Code Playgroud)


Men*_*its 6

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

(x for x in range(10) if x > 3).next()
Run Code Online (Sandbox Code Playgroud)


blu*_*ote 5

通过使用

(index for index, value in enumerate(the_iterable) if condition(value))
Run Code Online (Sandbox Code Playgroud)

可以检查the_iterable中第一个项的条件,并获得其索引,而无需评估the_iterable中的所有项.

要使用的完整表达式是

first_index = next(index for index, value in enumerate(the_iterable) if condition(value))
Run Code Online (Sandbox Code Playgroud)

这里first_index假定在上面讨论的表达式中标识的第一个值的值.


air*_*rne 5

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

if any((match := i) > 3 for i in range(10)):
    print(match)
Run Code Online (Sandbox Code Playgroud)


Dav*_*lli 5

以下是 3 个替代方案,并附有基准。

使用next()

单线:

values = list(range(1, 10000000))

value = next((x for x in values if x > 9999999), None)
Run Code Online (Sandbox Code Playgroud)

使用函数

这是使用函数的替代方法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)
Run Code Online (Sandbox Code Playgroud)

使用 lambda

这是一个在所有情况下都可以用于替换的函数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)
Run Code Online (Sandbox Code Playgroud)

基准测试

  • 功能:1x
  • 下一个:1.02x-1.05x
  • 拉姆达:> 3x

内存消耗相当。

这是基准