返回使传入函数为真的列表首个元素索引
`list.index(x)` 这个函数会返回列表中第一个值为 `x` 的项目的位置。
有没有一个类似于 `index()` 的函数,叫做 `list_func_index()`,它可以接收一个函数 `f()` 作为参数。这个函数 `f()` 会对列表中的每个元素 `e` 进行处理,直到 `f(e)` 返回 `True`。然后 `list_func_index()` 就会返回这个元素 `e` 的位置。
从代码的角度来看:
>>> def list_func_index(lst, func):
for i in range(len(lst)):
if func(lst[i]):
return i
raise ValueError('no element making func True')
>>> l = [8,10,4,5,7]
>>> def is_odd(x): return x % 2 != 0
>>> list_func_index(l,is_odd)
3
有没有更优雅的解决方案?(还有更好的函数名称吗)
8 个回答
保罗的答案是最好的,但这里有一个稍微不同的思路,主要是为了娱乐和教学目的……:
>>> class X(object):
... def __init__(self, pred): self.pred = pred
... def __eq__(self, other): return self.pred(other)
...
>>> l = [8,10,4,5,7]
>>> def is_odd(x): return x % 2 != 0
...
>>> l.index(X(is_odd))
3
简单来说,X
的作用是把“相等”的意思从普通的理解变成“满足这个条件”,这样就可以在各种需要检查相等的情况下使用条件。例如,你可以用更简短的方式来写代码,而不是用if any(is_odd(x) for x in l):
,而是用if X(is_odd) in l:
,等等。
值得使用吗?不一定,因为像@保罗那样更明确的方法同样方便(特别是当改用新的内置next
函数,而不是旧的、不太合适的.next
方法时,我在那条答案的评论中有提到),但在其他情况下,这种方法(或者类似的“改变相等的意思”的想法,可能还有其他比较器和/或哈希)可能是合适的。总的来说,了解这个想法是有必要的,以免有一天需要从头开始发明它。
一种可能的方法是使用内置的 enumerate 函数:
def index_of_first(lst, pred):
for i, v in enumerate(lst):
if pred(v):
return i
return None
通常我们会把你描述的那种函数称为“谓词”,它会对某个问题返回真或假。所以在我的例子中,我把它叫做 pred
。
我还认为返回 None
会更好,因为这才是问题的真实答案。如果需要,调用这个函数的人可以选择在 None
上出错。
你可以用一行代码来实现这个功能,使用生成器:
next(i for i,v in enumerate(l) if is_odd(v))
生成器的好处在于,它们只会计算你需要的部分。所以如果你想要前两个索引,几乎也是一样简单:
y = (i for i,v in enumerate(l) if is_odd(v))
x1 = next(y)
x2 = next(y)
不过,在最后一个索引之后,你会遇到一个叫做StopIteration的异常(这就是生成器的工作方式)。这在你“取前面”这种方法中也很方便,因为它能告诉你没有找到这样的值——而使用list.index()
函数在这里会引发ValueError
错误。