Python中的list.index()函数如果未找到内容不会抛出异常

129

如果列表中不存在该项,Python 的 list.index(x) 会抛出异常。是否有更好的方法可以避免使用异常处理?


1
取决于。你在意它在哪里吗? - Ignacio Vazquez-Abrams
3
最佳方法取决于在未找到任何内容的情况下您想要做什么。即使我们有返回-1的list.find,您仍需要测试以查看“i == -1”并采取某些操作。 - Raymond Hettinger
3
Raymond - 看起来应该由我决定我的代码能否处理 None 索引,而不是强制出现异常。但是,我还在学习如何编写 Pythonic 的代码... - Yarin
3
有时你知道可能会有遗漏的内容,而你只想使用 None 继续运行。如果缺少索引抛出异常是很有用的,但如果 [][0] 抛出异常,我也希望 [].index(0) 返回 None,或者至少允许使用 [].index(0, default=None)。这只需要1行代码而不是4行。 - NeilG
显示剩余2条评论
9个回答

126
如果您不关心匹配元素的位置,那么请使用:

如果您不关心匹配元素的位置,则使用:

found = x in somelist

如果你在意的话,那么请使用LBYL风格和条件表达式

i = somelist.index(x) if x in somelist else None

2
谢谢Raymond,我发现这是最简明的答案(再次更改默认值从-1到None,因为-1是有效的列表索引) - Yarin
2
谢谢Raymond,LBYL是我新学到的参考资料。 - Burhan Khalid
31
但是这种方法比仅运行index()要慢得多,不是吗?毕竟你必须查找两次:一次是为了存在性,一次是为了索引。这就是为什么C++容器没有exists(),只有find()的原因。 - frans
13
@frans说这需要两个查找,但是这里有另一种方法可以在一次遍历中完成任务:i = next((i for i, t in enumerate(somelist) if x == t), None) - AXO
3
即使只是一次遍历,对于内置类型来说速度会较慢,因为查找操作由Python而不是C代码执行。 - ivan_pozdeev
@ivan_pozdeev 很好的观点。有人可以测试一下吗? - jessexknight

11

如何为列表实现自己的索引?

class mylist(list):
  def index_withoutexception(self,i):
    try:
        return self.index(i)
    except:
        return -1

所以,您可以使用列表,并使用您的索引2,在出现错误的情况下返回您想要的内容。

您可以像这样使用它:

  l = mylist([1,2,3,4,5]) # This is the only difference with a real list
  l.append(4) # l is a list.
  l.index_withoutexception(19) # return -1 or what you want

注意,这可能会破坏一些代码:type(l) == list 在这里是 False - bfontaine
2
另外 - 我不确定,但我认为如果目标是避免引发异常(如果经常发生会很昂贵),那么这并没有实现它。它会返回-1,但内部仍然会引发异常,这仍然是昂贵的。 - logicOnAbstractions

7
TL;DR: 异常是你的朋友,并且是针对所述问题的最佳方法。
“宁可原谅别人,不要请求许可”(EAFP)
在评论中,OP澄清了他们的用例,实际上不知道索引是什么并不重要。 正如接受的答案所指出的那样,如果您不在意,则使用x in somelist是最佳答案。
但是,我会假设,正如原始问题所暗示的那样,您确实很关心索引是什么。 在这种情况下,我将指出,所有其他解决方案都需要扫描两次列表,这可能会带来很大的性能损失。
此外,正如崇高的Raymond Hettinger在评论中写道:
“即使我们有返回-1的list.find,你仍然需要测试看看i == -1并采取一些措施。”
因此,我对原始问题中应避免异常的假设提出反对意见。 我建议异常是你的朋友。 它们不会让你感到害怕,也不会效率低下,事实上你需要熟悉它们来编写好的代码。
因此,我认为最好的答案是简单地使用try-except方法:
try:
    i = somelist.index(x) 
except ValueError:
    # deal with it

"deal with it"的意思是做你需要做的事情: 将i设置为一个哨兵值,引发自己的异常,跟随不同的代码分支等等。
这是为什么Python原则宁愿请求原谅而不是征得许可(EAFP)是有意义的例子,与先看清再跳(LBYL)的if-then-else风格相反。

3
太对了!我以为我有这个问题,一开始认为其他解决方案之一不错。然后我再次查看了我的代码,意识到使用 try...except 更快捷和更自然,因为当然我仍然需要处理它.... - nealmcb
20
我笑得很大声。有时候领悟并不持久。我刚刚发表了上面的评论,没有注意到我两年前已经写了答案。只有当我试图给自己的答案投票时,SO才让我意识到这一点。 - nealmcb
5
例外情况昂贵。 - Andrew Scott Evans
@AndrewScottEvans 你能够量化异常的性能,并将其与其他答案进行比较吗?人们经常说过早优化是一个重大问题。因此,除非上下文是内部循环,否则效率必须显著低于其他方法,才有必要使用劣质的方法。 - nealmcb
4
你可能在想其他的编程语言。Python没有运行本机机器码,因此不需要将异常作为硬件中断实现。在C API中,异常是一个空值返回以及一个全局设置(每个线程)的异常对象:https://docs.python.org/3/c-api/exceptions.html。可以说,动态类型虚拟机的整个概念本身就会影响性能,但鉴于这一点,Python异常的速度与任何其他Python语句的速度并没有根本上的快慢之分。 - Jim Pivarski
显示剩余2条评论

6
编写一个函数来完成您所需的操作:
def find_in_iterable(x, iterable):
    for i, item in enumerate(iterable):
        if item == x:
            return i
    return None

如果你只需要知道这个项目是否存在,而不关心索引位置,你可以使用 in

x in yourlist

4
“-1”是一个有效的列表索引 - 你需要返回“None”。 - Yarin
@Yarin:我选择-1的原因是为了与现有的Python习惯用法保持一致,例如'abc'.find('x') == -1,但None也可以。我会更新我的答案。 - Mark Byers
1
是的,.find() 这样做很奇怪 - 看起来与 Python 的负索引不一致。 - Yarin

3
是的,可以。您可以类似于以下方式进行操作:

是的,可以。您可以类似于以下方式进行操作:

test = lambda l, e: l.index(e) if e in l else None

它的工作原理如下:

>>> a = ['a', 'b', 'c', 'g', 'c']
>>> test(a, 'b')
1
>>> test(a, 'c')
2
>>> test(a, 't')
None

基本上,test()会在给定的列表(第一个参数)中返回元素的索引(第二个参数),除非它未被找到(在这种情况下,它将返回None,但可以是任何你认为合适的内容)。


1
如果您不关心它在序列中的位置,只关心其是否存在,则使用in运算符。否则,编写一个函数来重构异常处理。
def inlist(needle, haystack):
  try:
    return haystack.index(needle)
  except ...:
    return -1

我已经发布了一个使用in检查存在性并最终返回索引的答案,但是我看到您知道该方法并决定使用可能会抛出的异常处理。我的问题是:为什么您选择捕获异常而不是首先检查haystack中是否存在needle?有什么原因吗? - Tadeck
@Tadeck:由于未写明的Python规则,“宁愿请求宽恕,也不要事先获得许可”,所以这样做更容易。 - Ignacio Vazquez-Abrams
3
谢谢 :) 我刚在Python词汇表中找到了这个词条:EAFP (Easier to Ask for Forgiveness than for Permission),它被描述为与LBYL (Look Before You Leap)相反的概念。事实上,EAFP被命名为“常见的Python编码风格”,因此它并不是那么不成文的规定 :) 再次感谢! - Tadeck
except ...: 行中,你实际上不需要 ... - swdev

1
我喜欢使用Web2pyList类,该类在其gluon包的storage模块中找到。 storage模块提供类似于列表(List)和字典(Storage)的数据结构,当元素未找到时不会引发错误。
首先下载web2py源代码,然后将gluon包文件夹复制粘贴到您的python安装的site-packages中。
现在试一下:
>>> from gluon.storage import List
>>> L = List(['a','b','c'])
>>> print L(2)
c
>>> print L(3) #No IndexError!
None

请注意,它也可以像普通列表一样工作:
>>> print L[3]

Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
l[3]
IndexError: list index out of range

def __call__(self, idx, df=None): return self[idx] if 0<=idx<len(self) else df好主意,谢谢。 - Winand

0

那篇文章是关于数组索引的,涉及到 IndexError: list index out of range 问题,而不是这里描述的 index() 方法和 ValueError: <value> is not in list 问题。 - nealmcb

0
希望这对你有所帮助
lst= ','.join('qwerty').split(',') # create list
i='a'  #srch string
lst.index(i) if i in lst else None

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接