要查找列表中项目的索引,请使用:
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没有内置的东西?
答案 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()
找到了项目 ,则无法返回
后来无法被天真的代码误解。
第一个条件排除所有正整数(包括零和sys.maxint
),第二个条件也排除负数(因为负索引是在Python中索引列表的有效方法)。除了整数之外的任何东西都可能在以后引发异常,如果后续代码假定它将会得到它。
无论该方法是引发异常还是返回特殊值,您通常都需要对该信息执行某些操作,并且:
try:
index = list.index(x)
except ValueError:
# do something
比这更具可读性:
index = list.index(x)
if index == some_special_value:
# do something
...在后一种情况下,忘记防范异常情况会导致代码无声地失败,可能会导致代码中其他地方出现混淆错误。
更糟糕的是,你必须记住或查找那个特殊值,对于这个以及任何其他类似的行为或函数。