列表中的项目的Python索引没有错误?

时间:2012-10-31 14:45:33

标签: python

  

可能重复:
  Python list.index question

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

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 land),它返回一个NSNotFound枚举(这只是一个max int,表示找不到该项)。

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

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

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

6 个答案:

答案 0 :(得分:18)

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

这个怎么样?

答案 1 :(得分:8)

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

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

答案 2 :(得分:7)

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

首先,有三种基本方法:

>>> 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循环进行迭代正是您想要做的事情。

答案 3 :(得分: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

答案 4 :(得分:0)

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

>>> from 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)实际上是列表的有效索引,它的确意味着“取最后一个值”,所以如果你试图将它作为一个特殊的错误代码使用,那么你最终会陷入困境一些讨厌,讨厌的bug。

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]

答案 5 :(得分:0)

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

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

    如果list.index()找到了项目

    ,则无法返回
  1. 后来无法被天真的代码误解。

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

    无论该方法是引发异常还是返回特殊值,您通常都需要对该信息执行某些操作,并且:

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

    比这更具可读性:

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

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

    更糟糕的是,你必须记住或查找那个特殊值,对于这个以及任何其他类似的行为或函数。