在python中处理list.index(可能不存在)的最佳方法?


113

我有看起来像这样的代码:

thing_index = thing_list.index(thing)
otherfunction(thing_list, thing_index)

好的,所以简化了,但是您知道了。现在thing可能实际上不在列表中,在这种情况下,我想通过-1 thing_index。在其他语言中index(),如果找不到该元素,这就是您期望返回的结果。实际上,它引发了一个错误ValueError

我可以这样做:

try:
    thing_index = thing_list.index(thing)
except ValueError:
    thing_index = -1
otherfunction(thing_list, thing_index)

但这感觉很脏,而且我不知道是否ValueError可以出于其他原因而提出该提议。我根据生成器函数提出了以下解决方案,但似乎有点复杂:

thing_index = ( [(i for i in xrange(len(thing_list)) if thing_list[i]==thing)] or [-1] )[0]

有没有一种更清洁的方法来实现同一目标?假设列表未排序。


4
“ ...在这种情况下,我想将-1传递为thing_index。” -这绝对是非Python的。如果操作不成功,则传递一个(无意义的)令牌值是令人讨厌的-异常确实是正确的方法。特别是因为thing_list[-1]是一个有效的表达式,表示列表中的最后一个条目。
Tim Pietzcker 2010年

@jellybean:facepalm ...发现Java编码器:P
Draemon 2010年

4
@Tim:有一种str.find方法可以做到这一点:-1当在主体中找不到针时返回。
SilentGhost 2010年

@Tim没有人会更好...然后这类似于dict [key] vs dict.get [key]
Draemon 2010年

@SilentGhost:嗯,很有趣。我可能需要更详细地研究这一点。str.index()如果未找到搜索字符串,则会引发异常。
Tim Pietzcker 2010年

Answers:


65

使用try-except子句没有任何“肮脏”。这是Python方式。ValueError只会由.index方法引发,因为这是您唯一的代码!

要回答的评论:
在Python,容易请求原谅比获得许可的理念已经非常成熟,并没有 index不会提高这种类型的错误的任何其他问题。并不是我能想到的。


29
当然,例外情况适用于特殊情况,而事实并非如此。如果异常比ValueError更具体,我就不会有这样的问题。
Draemon

1
我知道只能从该方法中抛出它,但是可以保证仅由于该原因而抛出它吗?不是不是我能想到索引失败的另一个原因。但是对于您可能没有想到的那些事情,难道不是例外吗?
Draemon

4
难道不是{}.get(index, '')pythonic吗?更不用说简短易读。
EstebanKüber2010年

1
我使用的字典[关键]当我想到键存在,dict.get(键)时,我不知道,我正在这里寻找等价的东西。返回None而不是-1很好,但是正如您自己评论的那样,str.find()返回-1,那么为什么不应该有list.find()做同样的事情呢?我不购买“ pythonic”参数
Draemon 2010年

3
但关键是,最pythonic的解决方案是使用try / except而不使用-1 sentinel值。IE浏览器应该重写otherfunction。另一方面,如果它还没有破裂,……
安德鲁·贾菲

53
thing_index = thing_list.index(elem) if elem in thing_list else -1

一条线。简单。没有例外。


35
是的,很简单,但这将进行两个线性搜索,而性能本身并不是问题,但这似乎是过分的。
Draemon 2010年

4
@Draemon:同意-将执行2次传递-但从一千行代码库来看,这不太可能成为瓶颈。:)您可以随时选择加入强制性解决方案for
埃米尔·伊万诺夫

与lambdindexOf = lambda item,list_ : list_.index(item) if item in list_ else -1 # OR None
Alaa

17

dict类型具有一个get函数,如果字典中不存在该键,则to的第二个参数get是它应返回的值。同样setdefaultdict存在,如果键存在,则返回值;否则,它将根据您的默认参数设置值,然后返回您的默认参数。

您可以扩展list类型以具有getindexdefault方法。

class SuperDuperList(list):
    def getindexdefault(self, elem, default):
        try:
            thing_index = self.index(elem)
            return thing_index
        except ValueError:
            return default

然后可以这样使用:

mylist = SuperDuperList([0,1,2])
index = mylist.getindexdefault( 'asdf', -1 )

6

使用的代码没有错ValueError。如果您想避免例外,这里还有另外一种说法:

thing_index = next((i for i, x in enumerate(thing_list) if x == thing), -1)

那是python 2.6吗?我知道我没有提到它,但是我使用的是2.5。这也许正是我在做2.6
Draemon

1
@Draemon:是的,next()函数存在于Python 2.6+中。但是对于2.5来说很容易实现,请参见Python 2.5的next()函数实现
jfs 2010年

4

这个问题是语言哲学之一。例如,在Java中,一直存在这样一种传统,即异常仅应仅在发生错误的“例外情况”中使用,而不是用于流控制。最初,这是出于性能原因,因为Java异常的速度很慢,但是现在这已成为公认的样式。

相反,Python一直使用异常来指示正常的程序流,就像ValueError我们在这里讨论的那样抛出异常。Python风格对此没有任何“污秽”,并且还有更多的来源。一个更常见的例子是StopIteration异常,它是由迭代器的next()方法引发的,以表示没有其他值。


其实,JDK投的方式太多的检查异常,所以我不知道哲学是实际应用到Java。我本身没有问题,StopIteration因为它已明确定义了异常的含义。ValueError太普通了。
Draemon 2010年

我指的是不应该将异常用于流控制的想法:c2.com/cgi/wiki?DontUseExceptionsForFlowControl,而不是Java拥有的已检查异常的数量,这是整个其他讨论的主题:mindview.net/Etc/Discussions / CheckedExceptions
Tendayi Mawushe


4

那😃呢?

li = [1,2,3,4,5] # create list 

li = dict(zip(li,range(len(li)))) # convert List To Dict 
print( li ) # {1: 0, 2: 1, 3: 2, 4:3 , 5: 4}
li.get(20) # None 
li.get(1)  # 0 

1

那这个呢:

otherfunction(thing_collection, thing)

而不是在函数接口中公开一些依赖实现的东西(如列表索引),而是传递集合和东西,然后让其他函数处理“成员资格测试”问题。如果将其他函数编写为与集合类型无关,则可能以以下内容开头:

if thing in thing_collection:
    ... proceed with operation on thing

如果thing_collection是一个列表,元组,集合或字典,它将起作用。

这可能比以下更清楚:

if thing_index != MAGIC_VALUE_INDICATING_NOT_A_MEMBER:

这是您在其他功能中已经拥有的代码。


1

像这样:

temp_inx = (L + [x]).index(x) 
inx = temp_inx if temp_inx < len(L) else -1

0

列表中的“ .index()”方法存在相同的问题。我对它引发异常的事实没有任何疑问,但是我强烈不同意它是一个非描述性ValueError的事实。我可以理解是否会是IndexError。

我可以看到为什么返回“ -1”也是一个问题,因为它是Python中的有效索引。但实际上,我从不期望“ .index()”方法返回负数。

这是一个班轮(好吧,这是一条相当长的线...),仅遍历列表一次,如果找不到该项目,则返回“无”。如果您愿意,将其重写为返回-1将是微不足道的。

indexOf = lambda list, thing: \
            reduce(lambda acc, (idx, elem): \
                   idx if (acc is None) and elem == thing else acc, list, None)

如何使用:

>>> indexOf([1,2,3], 4)
>>>
>>> indexOf([1,2,3], 1)
0
>>>

-2

我不知道为什么你应该认为它很脏...因为异常?如果您想要一个内衬,则为:

thing_index = thing_list.index(elem) if thing_list.count(elem) else -1

但是我建议不要使用它。我认为Ross Rogers解决方案是最好的解决方案,请使用对象封装您的喜好行为,不要尝试以牺牲可读性为代价将语言推向极限。


1
是的,因为有例外。您的代码会进行两次线性搜索,不是吗?在这里,性能并不是真的很重要。SuperDuperList解决方案很好,但在这种特定情况下似乎有些过头了。我想我最终会发现异常,但我想看看是否有一种更清洁的方法(就我的审美而言)。
Draemon

@Draemon:好吧,您将把代码封装到find()函数中,这将是干净的;)
SilentGhost 2010年

1
奇怪的是,我的回答有两次投票,而语义上相同的Emil Ivanov却是投票最多的人之一。这很可能是因为我的速度较慢,因为我使用了count()而不是“ in”运算符...至少有一条评论说那会很棒,尽管:-)
Alan Franzoni

-2

我建议:

if thing in thing_list:
  list_index = -1
else:
  list_index = thing_list.index(thing)

2
该解决方案的问题在于“ -1”是列表中的有效索引(最后一个索引;从末尾开始的第一个)。更好的方法是在条件的第一个分支中返回False。
FanaticD 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.