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


71

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

是否有一个函数,list_func_index()类似于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

有没有更优雅的解决方案?(以及该函数的更好的名称)

Answers:


108

您可以使用生成器在单行代码中做到这一点:

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。


13
这不是混淆性的-至少,它不比使用map(f, seq)代替而是混淆性更大[f(x) for x in seq]。换句话说,这是惯用法。与其他习语一样,要成为词汇表的一部分,它并不容易。
罗伯特·罗斯尼

2
只是提醒您StopIteration是否可能不满足最终条件。
payala

3
小提示:next接受第二个参数,如果不匹配,将返回该参数,而不是引发StopIteration
bgusach'4

15

一种可能是内置的枚举功能:

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

通常将类似您所描述的函数称为“谓词”。对于某些问题,它返回true或false。所以才叫它pred在示例中。

我也认为返回表格会更好None,因为这是问题的真正答案。None如果需要,呼叫者可以选择在上爆炸。


1
更优雅,更
出名

我认为,如果找不到给定值,OP希望模仿索引引发ValueError的行为。
PaulMcG,2009年

+1枚举是我的最爱。我不记得上一次我实际上必须在python中使用老式C方式维护索引变量的情况。
Mattias Nilsson,2009年

13

@Paul接受的答案是最好的,但这是一个侧面思考的变体,主要用于娱乐和指导目的...:

>>> 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:等等。

值得使用吗?当像@Paul那样的更明确的方法同样方便时(特别是当更改为使用新的,闪亮的内置next函数而不是较旧的,不太合适的.next方法,正如我在对该答案的评论中建议的那样时),但是在其他情况下,它(或“调整相等含义”的思想的其他变体,以及其他比较器和/或哈希)可能是合适的。通常,值得了解这个主意,避免有一天不得不从头开始发明它;-)。


好一个!但是我们将“命名” X是什么?像“钥匙”之类的东西?因为它让我想起了l.sort(key = fn)。
保罗

您几乎可以称其为“等于”,因此该行显示为l.index(Equals(is_odd))
tgray

3
我认为亚历克斯(暗示)建议的Satisfies是一个好名字。
罗伯特·罗斯尼

@Robert,我喜欢满意!
Alex Martelli

很抱歉,请问如何在生成列表引用的所有奇数元素的生成器中表达和使用“满意”?(我想还没有得到发电机的支持,...)ref = [8,10,4,5,7] def is_odd(x):返回x%2!= 0类满意度(对象):def __init __(self,pred):self.pred = pred def __eq __(self,test_this):返回self.pred(test_this)print ref.index(Satisfies(is_odd))#>>> 3
落在

4

不是一个功能,但是您可以轻松完成:

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

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

>>> from itertools import imap, ifilter
>>> from operator import itemgetter
>>> test = lambda c: c == 'x'
>>> data = ['a', 'b', 'c', 'x', 'y', 'z']
>>> ifilter(itemgetter(1), enumerate(imap(test, data))).next()[0]
3
>>> 

itertools虽然仅使用生成器表达式可能更具可读性。

注意在Python3中,mapfilter返回惰性迭代器,您可以使用:

from operator import itemgetter
test = lambda c: c == 'x'
data = ['a', 'b', 'c', 'x', 'y', 'z']
next(filter(itemgetter(1), enumerate(map(test, data))))[0]  # 3

2
不幸的是,这会评估整个列表-最好有一个短路解决方案,即找到第一个匹配项时立即返回。
PaulMcG,2009年

2

亚历克斯答案的变化。这样避免了X每次都要使用is_odd或谓词时都不必键入

>>> class X(object):
...     def __init__(self, pred): self.pred = pred
...     def __eq__(self, other): return self.pred(other)
... 
>>> L = [8,10,4,5,7]
>>> is_odd = X(lambda x: x%2 != 0)
>>> L.index(is_odd)
3
>>> less_than_six = X(lambda x: x<6)
>>> L.index(less_than_six)
2

1

您可以通过列表理解来做到这一点:

l = [8,10,4,5,7]
filterl = [a for a in l if a % 2 != 0]

然后filterl将返回满足表达式%2!= 0的列表的所有成员。我想说一个更优雅的方法...


您是否可以将答案编辑为更像具有列表和参数功能的OP功能?
quamrana

6
错了 它返回值列表,而不是单个索引。
递归

filterl = [如果是is_odd(a),则为l中的a]
Vincent Osinga,2009年

1
我说过,您可以通过列表理解来做到这一点,并且它还返回一个列表。只是想提供一个不同的选择,因为我不确定头巾的确切问题是什么。
Vincent Osinga,2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.