Python:返回列表的第一个元素的索引,使得传递的函数为true

ban*_*ana 48 python indexing function list

list.index(x)函数返回值为的第一个项的列表中的索引x.

是否有一个函数,list_func_index()类似于index()具有函数的函数f(),作为参数.该函数在列表的f()每个元素上运行e,直到 f(e)返回True.然后list_func_index()返回索引e.

Codewise:

>>> 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
Run Code Online (Sandbox Code Playgroud)

有更优雅的解决方案吗?(以及该功能的更好名称)

Pau*_*aul 86

您可以使用生成器在单行中执行此操作:

next(i for i,v in enumerate(l) if is_odd(v))
Run Code Online (Sandbox Code Playgroud)

关于生成器的好处是它们只能计算到所需的数量.因此,请求前两个索引(几乎)同样简单:

y = (i for i,v in enumerate(l) if is_odd(v))
x1 = next(y)
x2 = next(y)
Run Code Online (Sandbox Code Playgroud)

但是,期望在最后一个索引之后出现StopIteration异常(这就是生成器的工作方式).这在你的"先取"方法中也很方便,要知道没有找到这样的值--- list.index()函数会抛出ValueError.

  • 这不是混淆 - 或者至少,它不比使用`map(f,seq)`而不是'[f(x)for x in seq]`更加混淆.换句话说,它是惯用的.和其他习语一样,直到它成为你词汇的一部分才是直截了当的. (12认同)
  • 是否有人会开始使用正确的`2.6` /`3.*`成语,即`next(i for i,v in enumerate(L)if pred(v))`?!它更短,您可以添加第二个参数来代替StopIteration.好吧,无论如何+1,因为它显然比编码输出循环更优雅和惯用,即使在这种不完美的形式. (10认同)
  • 小提示:“next”接受第二个参数,在不匹配的情况下将返回该参数,而不是引发“StopIteration”。 (7认同)
  • 如果可能不满足结束条件,请提醒您捕获“StopIteration”。 (2认同)

Ale*_*lli 12

@保罗接受的答案是最好的,但这里有一个侧面思维的变体,主要用于娱乐和教学目的......:

>>> 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
Run Code Online (Sandbox Code Playgroud)

本质上,其X目的是将"相等"的含义从正常的"改变为满足此谓词",从而允许在各种情况下使用谓词,这些情境被定义为检查相等性 - 例如,它会也让你编码,而不是if any(is_odd(x) for x in l):更短if X(is_odd) in l:,等等.

值得使用?当@Paul采用的更明确的方法同样方便时(特别是当改为使用新的,闪亮的内置next函数而不是旧的,不太合适的.next方法时,正如我在对该答案的评论中所建议的那样),但是在其他情况下(或者其他变体的想法"调整平等的含义",也许其他比较器和/或散列)可能是合适的.大多数情况下,值得了解这个想法,以避免有一天从头开始发明它;-).

  • 我认为亚历克斯(隐含地)建议的“满足”是一个很好的名字。 (3认同)

Jon*_*erg 11

一种可能性是内置的枚举函数:

def index_of_first(lst, pred):
    for i,v in enumerate(lst):
        if pred(v):
            return i
    return None
Run Code Online (Sandbox Code Playgroud)

通常将您描述的函数称为"谓词"; 某些问题会返回true或false.这就是我pred在我的例子中称之为的原因.

我也认为返回会有更好的形式None,因为这是问题的真正答案.None如果需要,呼叫者可以选择爆炸.


Ste*_*osh 6

不是一个单一的函数,但你可以在 Python 2 中非常轻松地完成它:

>>> test = lambda c: c == 'x'
>>> data = ['a', 'b', 'c', 'x', 'y', 'z', 'x']
>>> map(test, data).index(True)
3
Run Code Online (Sandbox Code Playgroud)

如果您不想立即评估整个列表,您可以使用 itertools,但它并不那么漂亮:

>>> from itertools import imap, ifilter
>>> from operator import itemgetter
>>> ifilter(itemgetter(1), enumerate(imap(test, data))).next()[0]
3
Run Code Online (Sandbox Code Playgroud)

仅使用生成器表达式可能比不过更具可读性itertools

注意在Python3中,map返回filter惰性迭代器,你可以使用:

>>> from operator import itemgetter
>>> next(filter(itemgetter(1), enumerate(map(test, data))))[0]
3
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这会评估整个列表 - 如果有一个短路的解决方案,即在找到第一个匹配项时立即返回,那就太好了。 (2认同)