我想从符合条件的列表中获取第一项。重要的是,生成的方法不会处理整个列表,这可能非常大。例如,以下功能就足够了:
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
但是,我不能想到一个好的内置/单线程让我这样做。如果我不需要,我不特别想复制这个功能。是否有内置方法可以使第一个项目符合条件?
答案 0 :(得分:361)
在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
构建的行-in在Python 2.6及更高版本中。
如果你做关心,你在Q中首先指出的函数包装内容似乎是最好的,虽然你提出的函数实现很好,但你也可以使用{{1} },一个itertools
循环,或一个genexp,或一个for...: break
作为函数的主体,正如各种答案所暗示的那样。这些替代品中没有太多附加值,所以我会选择你最初提出的简单版本。
答案 1 :(得分: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))
答案 2 :(得分:13)
与使用ifilter
类似,您可以使用生成器表达式:
>>> (x for x in xrange(10) if x > 5).next()
6
在任何一种情况下,您可能希望捕获StopIteration
,以防没有元素满足您的条件。
从技术上讲,我想你可以这样做:
>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
...
>>> foo
6
这将避免必须进行try/except
阻止。但这似乎有点模糊和滥用语法。
答案 3 :(得分:12)
我爱this answer。但是,由于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
答案 4 :(得分:7)
Python 3中最有效的方法是以下之一(使用类似示例):
next(i for i in range(100000000) if i == 1000)
警告:该表达式也适用于Python 2,但在示例中使用了range
,它返回Python 3中的可迭代对象而不是像Python 2这样的列表(如果你想要的话)在Python 2中构造一个可迭代的代替使用xrange
。
请注意,表达式避免在理解表达式next([i for ...])
中构造一个列表,这会导致在过滤元素之前创建包含所有元素的列表,并且会导致处理整个选项,而不是停止迭代一次i == 1000
。
next(filter(lambda i: i == 1000, range(100000000)))
警告:这在Python 2中不起作用,甚至将range
替换为xrange
,因为filter
创建了一个列表而不是迭代器(效率低下) ),next
函数仅适用于迭代器。
如其他响应中所述,如果要避免在未满足条件时引发异常,则必须向函数next
添加额外参数。
next(filter(lambda i: i == 1000, range(100000000)), False)
使用此样式,您需要使用()
包围理解表达式以避免使用SyntaxError: Generator expression must be parenthesized if not sole argument
:
next((i for i in range(100000000) if i == 1000), False)
答案 5 :(得分:6)
itertools
模块包含迭代器的过滤函数。可以通过调用next()
来获取筛选迭代器的第一个元素:
from itertools import ifilter
print ifilter((lambda i: i > 3), range(10)).next()
答案 6 :(得分:6)
我会写这个
next(x for x in xrange(10) if x > 3)
答案 7 :(得分:6)
对于旧版本的Python,下一个内置不存在:
(x for x in range(10) if x > 3).next()
答案 8 :(得分:5)
使用
(index for index, value in enumerate(the_iterable) if condition(value))
可以检查 the_iterable 中第一项的值的条件,并获取其索引无需评估 the_iterable 中的所有项目。
要使用的完整表达式是
first_index = next(index for index, value in enumerate(the_iterable) if condition(value))
此处 first_index 假定上面讨论的表达式中标识的第一个值。
答案 9 :(得分:3)
对于使用 Python 3.8 或更高版本的任何人,我建议使用 PEP 572 -- Assignment Expressions 中所述的“赋值表达式”。
if any((match := i) > 3 for i in range(10)):
print(match)
答案 10 :(得分:1)
这个问题已经有了很好的答案。我只添加了两分钱,因为我在这里试图找到解决我自己问题的方法,这与OP非常相似。
如果您想使用生成器找到符合条件的第一个项目的INDEX,您可以这样做:
next(index for index, value in enumerate(iterable) if condition)
答案 11 :(得分:0)
由于您已请求内置单行,这将避免StopIteration
异常的问题,但它要求您的iterable很小,因此您可以将其转换为列表,因为这是我所知道的唯一一个将吞下StopIteration并让你偷看价值的构造:
(lambda x:x[0] if x else None)(list(y for y in ITERABLE if CONDITION))
(如果没有元素匹配,您将获得None
而不是StopIteration
例外。)
答案 12 :(得分:0)
您还可以在Numpy中使用argwhere
函数。例如:
i)在“ helloworld”中找到第一个“ l”:
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()
ii)查找第一个随机数> 0.1
import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_first = i.min()
iii)找到最后一个随机数> 0.1
import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_last = i.max()
答案 13 :(得分:-2)
Oneliner:
thefirst = [i for i in range(10) if i > 3][0]
如果您不确定任何元素是否符合条件,则应将其与try/except
括起来,因为[0]
可以引发IndexError
。
答案 14 :(得分:-2)
在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