39

要在列表中查找项目的索引,请使用:

list.index(x)
Return the index in the list of the first item whose value is x. 
It is an error if there is no such item.

这对我来说似乎有点奇怪,如果找不到该项目会引发错误。我来自哪里(Objective-C 领域),它返回一个 NSNotFound 枚举(它只是一个最大 int,表示未找到该项目)。

所以我做了一些丑陋的事情来解决这个问题:

index = 0
for item in self.items:
   if item.id == desired_id:
        return index
    index = index + 1
 return -1

我使用 -1 表示未找到该项目。有什么更好的方法来做到这一点,为什么 Python 没有内置这样的东西?

4

6 回答 6

36
a = [1]
try:
    index_value = a.index(44)
except ValueError:
    index_value = -1

这个怎么样?

于 2012-10-31T14:48:38.633 回答
12

返回 -1 不是一个好主意,因为它是 Python 中的有效索引(请参阅Python list.index throws exception when index not found)。

可能最好捕获索引错误并采取相应措施。

于 2012-10-31T14:48:30.083 回答
11

我同意所指出的一般解决方案,但我想更多地研究答案和评论中解释的方法,看看哪种方法更有效以及在哪些情况下。

首先,三种基本方法:

>>> def my_index(L, obj):
...     for i, el in enumerate(L):
...             if el == obj:
...                     return i
...     return -1
... 
>>> def my_index2(L, obj):
...     try:
...             return L.index(obj)
...     except ValueError:
...             return -1
... 
>>> def my_index3(L, obj):
...     if obj in L:
...             return L.index(obj)
...     return -1
... 

第一个和第二个解决方案只扫描列表一次,因此您可能认为它们比第三个更快,因为它扫描列表两次。那么让我们看看:

>>> timeit.timeit('my_index(L, 24999)', 'from __main__ import my_index, L', number=1000)
1.6892211437225342
>>> timeit.timeit('my_index2(L, 24999)', 'from __main__ import my_index2, L', number=1000)
0.403195858001709
>>> timeit.timeit('my_index3(L, 24999)', 'from __main__ import my_index3, L', number=1000)
0.7741198539733887

好吧,第二个确实是最快的,但是您可以注意到第一个比第三个慢得多,即使它只扫描列表一次。如果我们增加列表的大小,事情并没有太大变化:

>>> L = list(range(2500000))
>>> timeit.timeit('my_index(L, 2499999)', 'from __main__ import my_index, L', number=100)
17.323430061340332
>>> timeit.timeit('my_index2(L, 2499999)', 'from __main__ import my_index2, L', number=100)
4.213982820510864
>>> timeit.timeit('my_index3(L, 2499999)', 'from __main__ import my_index3, L', number=100)
8.406487941741943

第一个仍然慢 2 倍。

如果我们搜索不在列表中的内容,第一个解决方案的情况会变得更糟:

>>> timeit.timeit('my_index(L, None)', 'from __main__ import my_index, L', number=100)
19.055058002471924
>>> timeit.timeit('my_index2(L, None)', 'from __main__ import my_index2, L', number=100)
5.785136938095093
>>> timeit.timeit('my_index3(L, None)', 'from __main__ import my_index3, L', number=100)
5.46164608001709

正如您在这种情况下所看到的,第三种解决方案甚至胜过第二种解决方案,并且两者都比 python 代码快了近 4 倍。根据您期望搜索失败的频率,您希望选择#2 或#3(即使在 99% 的情况下,#2 更好)。

作为一般规则,如果您想为 CPython 优化某些东西,那么您希望尽可能多地“在 C 级别”进行迭代。在您的示例中,使用 for 循环进行迭代正是您不想做的事情。

于 2012-10-31T15:08:00.290 回答
1

使用异常处理,list.index引发ValueError,以便您可以捕获该异常:

一个简单的例子:

In [78]: lis=[1,2,3,4]

In [79]: for i in range(-1,6):
    try:
        print lis.index(i)
    except ValueError:    
        print i,"not found"

-1 not found
0 not found
0
1
2
3
5 not found
于 2012-10-31T14:48:42.720 回答
1

这种行为有一个明确的原因:

>>> import this
...
In the face of ambiguity, refuse the temptation to guess.
...

对于系统应该如何响应像“NSNotFound”这样的对象没有明确的解释,所以你必须拒绝猜测,然后为此实现一个特殊功能就变得毫无用处了。

想想如果我尝试做这样的事情会发生什么:

[ objective.index(i)+1 for i in reference_list ]

NSNotFound 加 1 是什么意思?做类似的事情不是更简单:

[ objective.index(i)+1 for i in reference_list if i in objective ]

而且,-1实际上是一个有效的列表索引,意思是“取最后一个值”,所以如果你试图将它用作一个特殊的错误代码,很可能你最终会遇到一些讨厌的、讨厌的错误。

Guido 的设计感非常强,不要小看他 ;)

也就是说,如果您仍然需要类似的东西,您可以尝试使用以下代码:

class NotFoundError(Exception):
    def __init__(self,container,index):
        self.message = "object "+str(index)+" not found on "+str(container)
        self.container = container
        self.index = index
    def __str__(self):
        return self.message

def getindex(cont,idx):
    try:
        return cont.index(idx)
    except:
        return NotFoundError(cont,idx)

a = [1,2]

print getindex(a,3)
#object 3 not found on [1, 2]
于 2012-10-31T15:08:43.177 回答
-1

最好将其视为“引发异常”而不是“引发错误”。

Python 中的异常不仅针对错误,而且针对异常情况——因此得名。如果list.index()返回了一些特殊值,它需要是一个

  1. 无法退回已list.index()找到该项目

  2. 随后不能被天真的代码误解。

第一个条件排除所有正整数(包括零和sys.maxint),第二个条件也排除负整数(因为负索引是在 Python 中索引列表的有效方法)。无论如何,除整数以外的任何东西都可能在以后引发异常,如果后续代码假设这就是它会得到的。

无论该方法是引发异常还是返回特殊值,您通常都需要对这些信息进行处理,并且:

try:
    index = list.index(x)
except ValueError:
    # do something

比这更具可读性:

index = list.index(x)
if index == some_special_value:
    # do something

...在后一种情况下,忘记防范异常情况会导致代码静默失败,可能会导致代码中其他地方出现混淆错误。

更糟糕的是,对于 this 以及任何其他类似的方法或函数,您必须记住或查找该特殊值是什么。

于 2012-10-31T15:09:36.830 回答