返回使传入函数为真的列表首个元素索引

129 投票
8 回答
99401 浏览
提问于 2025-04-15 15:49

`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 个回答

18

保罗的答案是最好的,但这里有一个稍微不同的思路,主要是为了娱乐和教学目的……:

>>> 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方法时,我在那条答案的评论中有提到),但在其他情况下,这种方法(或者类似的“改变相等的意思”的想法,可能还有其他比较器和/或哈希)可能是合适的。总的来说,了解这个想法是有必要的,以免有一天需要从头开始发明它。

24

一种可能的方法是使用内置的 enumerate 函数:

def index_of_first(lst, pred):
    for i, v in enumerate(lst):
        if pred(v):
            return i
    return None

通常我们会把你描述的那种函数称为“谓词”,它会对某个问题返回真或假。所以在我的例子中,我把它叫做 pred

我还认为返回 None 会更好,因为这才是问题的真实答案。如果需要,调用这个函数的人可以选择在 None 上出错。

174

你可以用一行代码来实现这个功能,使用生成器:

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错误。

撰写回答