我想从匹配条件的列表中获得第一项。产生的方法不能处理整个列表,这一点很重要,因为列表可能相当大。例如,以下函数就足够了:
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()不是最快的方法。
from timeit import default_timer as timer
# Is set irreflexive?
def a():
return frozenset((x3, x3) for x3 in set([x1[x2] for x2 in range(2) for x1 in value]) if (x3, x3) in value) == frozenset()
def b():
return next((False for x1 in value if (x1[0], x1[0]) in value or (x1[1], x1[1]) in value), True)
def c():
for x1 in value:
if (x1[0], x1[0]) in value or (x1[1], x1[1]) in value:
return False
return True
times = 1000000
value = frozenset({(1, 3), (2, 1)})
start_time = timer()
for x in range(times):
a()
print("a(): Calculation ended after " + str(round((timer() - start_time) * 1000) / 1000.0) + " sec")
start_time = timer()
for x in range(times):
b()
print("b(): Calculation ended after " + str(round((timer() - start_time) * 1000) / 1000.0) + " sec")
start_time = timer()
for x in range(times):
c()
print("c(): Calculation ended after " + str(round((timer() - start_time) * 1000) / 1000.0) + " sec")
结果:
Calculation ended after 1.365 sec
Calculation ended after 0.685 sec
Calculation ended after 0.493 sec
下面是带有基准的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
内存消耗相同。
这就是基准。
作为一个可重用、文档化和测试的函数
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