在列表中查找元素的索引
在列表中查找元素的索引
假设有一个列表[\"foo\", \"bar\", \"baz\"]
和列表中的一个元素\"bar\"
,我该如何获取它的索引1
?
大多数答案都解释了如何找到单个索引,但它们的方法如果该项在列表中出现多次,则不会返回多个索引。使用enumerate()
:
for i, j in enumerate(['foo', 'bar', 'baz']): if j == 'bar': print(i)
index()
函数仅返回第一个出现的位置,而enumerate()
返回所有出现的位置。
作为列表理解式:
[i for i, j in enumerate(['foo', 'bar', 'baz']) if j == 'bar']
这里还有另一个使用itertools.count()
的简单解决方案(与enumerate()
方法几乎相同):
from itertools import izip as zip, count # izip for maximum efficiency [i for i, j in zip(count(), ['foo', 'bar', 'baz']) if j == 'bar']
对于较大的列表,这比使用enumerate()
更有效:
$ python -m timeit -s "from itertools import izip as zip, count" "[i for i, j in zip(count(), ['foo', 'bar', 'baz']*500) if j == 'bar']" 10000 loops, best of 3: 174 usec per loop $ python -m timeit "[i for i, j in enumerate(['foo', 'bar', 'baz']*500) if j == 'bar']" 10000 loops, best of 3: 196 usec per loop
>>> ["foo", "bar", "baz"].index("bar") 1
请参阅文档中的内置.index()
方法的说明:
list.index(x[, start[, end]])
返回列表中第一个值等于x的元素的基于零的索引。如果不存在这样的元素,则会引发
ValueError
异常。可选参数start和end的解释与切片符号中的相同,并用于限制搜索到列表的特定子序列。返回的索引是相对于完整序列的开头而不是开始参数计算的。
注意事项
线性时间复杂度
index
方法按顺序检查列表中的每个元素,直到找到匹配项为止。如果列表很长,且无法保证该值在列表开头附近,这可能会使代码变慢。
只有通过使用不同的数据结构才能完全避免这个问题。然而,如果已知该元素在列表的某个部分内,可以使用start
和end
参数来缩小搜索范围。
例如:
>>> import timeit >>> timeit.timeit('l.index(999_999)', setup='l = list(range(0, 1_000_000))', number=1000) 9.356267921015387 >>> timeit.timeit('l.index(999_999, 999_990, 1_000_000)', setup='l = list(range(0, 1_000_000))', number=1000) 0.0004404920036904514
第二个调用比第一个调用快数个数量级,因为它只需要搜索10个元素,而不是全部100万个元素。
只返回第一个匹配项的索引
index
方法按顺序搜索列表直到找到匹配项为止,并停止搜索。如果该值可能出现多次,并且需要所有索引,则index
无法解决问题:
>>> [1, 1].index(1) # the `1` index is not found. 0
相反,可以使用列表推导式或生成器表达式进行搜索,并使用enumerate
获得索引:
>>> # A list comprehension gives a list of indices directly: >>> [i for i, e in enumerate([1, 2, 1]) if e == 1] [0, 2] >>> # A generator comprehension gives us an iterable object... >>> g = (i for i, e in enumerate([1, 2, 1]) if e == 1) >>> # which can be used in a `for` loop, or manually iterated with `next`: >>> next(g) 0 >>> next(g) 2
如果只有一个匹配项,则列表推导和生成器表达式技术仍然有效,并且更具通用性。
如果没有匹配项则会引发异常
如上面的文档所述,使用 .index
会在列表中找不到搜索的值时引发异常:
>>> [1, 1].index(2) Traceback (most recent call last): File "", line 1, in ValueError: 2 is not in list
如果这是一个问题,可以使用 item in my_list
显式地进行第一次检查,或根据需要使用 try
/except
来处理异常。
显式检查简单易读,但必须第二次迭代列表。有关此选择的更多指导,请参见 Python 中的 EAFP 原则是什么?。