我想从匹配条件的列表中获得第一项。产生的方法不能处理整个列表,这一点很重要,因为列表可能相当大。例如,以下函数就足够了:
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
但是,我想不出一个好的内置/单行程序来让我这样做。如果没有必要,我不想复制这个函数。是否有一种内置的方法来获取匹配条件的第一项?
如果你不想使用next(),你可以使用unpacking:
>>> a, *_ = filter(lambda e: e == 10, [7,8,9,10,11,12])
>>> a
10
>>> _
[]
>>> a, *_ = filter(lambda e: e == 1000, [7,8,9,10,11,12])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected at least 1, got 0)
注意,使用filter()相当于写入Python Docs (item for item in iterable if condition)。
如果你需要对边缘情况的支持,你可以这样写:
>>> a, *_ = [e for e in [7,8,9,10,11,12] if e == 1000] or [None]
>>> a
None
>>> _
[]
该死的例外!
我喜欢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
如果你不想使用next(),你可以使用unpacking:
>>> a, *_ = filter(lambda e: e == 10, [7,8,9,10,11,12])
>>> a
10
>>> _
[]
>>> a, *_ = filter(lambda e: e == 1000, [7,8,9,10,11,12])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected at least 1, got 0)
注意,使用filter()相当于写入Python Docs (item for item in iterable if condition)。
如果你需要对边缘情况的支持,你可以这样写:
>>> a, *_ = [e for e in [7,8,9,10,11,12] if e == 1000] or [None]
>>> a
None
>>> _
[]
作为一个可重用、文档化和测试的函数
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
Python 2.6+和Python 3:
如果你想在没有找到匹配的元素时引发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)
注意,在这种情况下,在生成器表达式周围需要一对额外的圆括号-当生成器表达式不是唯一的参数时,就需要它们。
我看到大多数答案都坚决地忽略了下一个内置版本,所以我假设出于某种神秘的原因,他们100%专注于2.5及以上版本——没有提到python版本的问题(但我在回答中没有看到提到下一个内置版本的问题,这就是为什么我认为有必要自己提供一个答案——至少“正确版本”的问题会以这种方式记录下来;-)。
Python <= 2.5
如果迭代器立即结束,迭代器的.next()方法立即引发StopIteration——也就是说,对于您的用例,如果可迭代对象中没有项满足条件。如果你不在乎(也就是说,你知道至少有一个令人满意的项),那么只需使用.next()(最好用于genexp, Python 2.6或更好的下一个内置代码行)。
如果你真的关心,就像你在Q中第一次指出的那样,将东西包装在函数中似乎是最好的,而你提出的函数实现也很好,你可以选择使用itertools,一个for…: break循环,或genexp,或try/except StopIteration作为函数体,正如各种答案所建议的那样。这些替代方案都没有多少附加价值,所以我会选择你最初提出的极其简单的版本。
通过使用
(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假设上面讨论的表达式中标识的第一个值的值。